diff --git a/.gitignore b/.gitignore
index aa35b98a520e40a764474b7ebb4e13c807c5ff40..77d49d817d45162549968cadda3fe9259939ed23 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,9 @@ castor/scheduler/Makefile
 castor/stager/daemon/stagerd
 castor/tape/mighunter/mighunterd
 castor/tape/rechandler/rechandlerd
+castor/tape/rmc/castor-tape-acs-dismount
+castor/tape/rmc/castor-tape-acs-mount
+castor/tape/rmc/castor-tape-acs-queryvolume
 castor/tape/tapebridge/tapebridged
 castor/tape/tapegateway/tapegatewayd
 castor/tape/tpcp/dumptp
diff --git a/castor/tape/Imakefile b/castor/tape/Imakefile
index c7fa71a6725e1f51d6a3e993232982270975ceef..4365f3152fe3898d5d7f837d64263bd8f6901dd5 100644
--- a/castor/tape/Imakefile
+++ b/castor/tape/Imakefile
@@ -29,3 +29,4 @@ include LOCAL_PATH/utils/Makefile
 include LOCAL_PATH/legacymsg/Makefile
 include LOCAL_PATH/net/Makefile
 include LOCAL_PATH/tapebridge/Makefile
+COMM include LOCAL_PATH/rmc/Makefile
diff --git a/castor/tape/rmc/Acs.cpp b/castor/tape/rmc/Acs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9e907143ca5e1a64b32bf3f651a5947c9c93828d
--- /dev/null
+++ b/castor/tape/rmc/Acs.cpp
@@ -0,0 +1,162 @@
+/******************************************************************************
+ *                 castor/tape/rmc/Acs.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/Acs.hpp"
+#include "castor/tape/utils/utils.hpp"
+
+#include <iomanip>
+#include <sstream>
+#include <stdint.h>
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::Acs::~Acs() throw() {
+}
+
+//------------------------------------------------------------------------------
+// str2DriveId
+//------------------------------------------------------------------------------
+DRIVEID castor::tape::rmc::Acs::str2DriveId(const std::string &str)
+  const throw(castor::exception::InvalidArgument) {
+  std::vector<std::string> components;
+  utils::splitString(str, ':', components);
+
+  // The drive ID should consist of 4 components: ACS, LSM, Panel and Transport
+  if(4 != components.size()) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Invalid number of components in drive ID"
+      ": expected=4, actual=" << components.size();
+    throw(ex);
+  }
+
+  const std::string &acsStr = components[0];
+  const std::string &lsmStr = components[1];
+  const std::string &panStr = components[2];
+  const std::string &drvStr = components[3];
+
+  // Each of the 4 components must be between 1 and than 3 characters long
+  if(1 > acsStr.length() ||  3 < acsStr.length()) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Invalid ACS string length"
+      ": expected=1..3, actual=" << acsStr.length();
+    throw(ex);
+  }
+  if(1 > lsmStr.length() || 3 < lsmStr.length()) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Invalid LSM string length"
+      ": expected=1..3, actual=" << lsmStr.length();
+    throw(ex);
+  }
+  if(1 > panStr.length() || 3 < panStr.length()) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Invalid panel string length"
+      ": expected=1..3, actual=" << panStr.length();
+    throw(ex);
+  }
+  if(1 > drvStr.length() || 3 < drvStr.length()) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Invalid drive string length"
+      ": expected=1..3, actual=" << drvStr.length();
+    throw(ex);
+  }
+
+  // Each of the 4 components must only contain numerals
+  if(!onlyContainsNumerals(acsStr)) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "ACS must only contain numerals: value=" << acsStr;
+    throw(ex);
+  }
+  if(!onlyContainsNumerals(lsmStr)) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "LSM must only contain numerals: value=" << acsStr;
+    throw(ex);
+  }
+  if(!onlyContainsNumerals(panStr)) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Panel must only contain numerals: value=" << acsStr;
+    throw(ex);
+  }
+  if(!onlyContainsNumerals(drvStr)) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Drive/Transport must only contain numerals: value=" <<
+      acsStr;
+    throw(ex);
+  }
+
+  DRIVEID driveId;
+  driveId.panel_id.lsm_id.acs = (ACS)atoi(acsStr.c_str());
+  driveId.panel_id.lsm_id.lsm = (LSM)atoi(lsmStr.c_str());
+  driveId.panel_id.panel = (PANEL)atoi(panStr.c_str());
+  driveId.drive = (DRIVE)atoi(drvStr.c_str());
+
+  return driveId;
+}
+
+//------------------------------------------------------------------------------
+// str2Volid
+//------------------------------------------------------------------------------
+VOLID castor::tape::rmc::Acs::str2Volid(const std::string &str) const
+  throw(castor::exception::InvalidArgument) {
+  if(EXTERNAL_LABEL_SIZE < str.length()) {
+    castor::exception::InvalidArgument ex;
+    ex.getMessage() << "Failed to convert string to volume identifier"
+      ": String is longer than the " << EXTERNAL_LABEL_SIZE <<
+      " character maximum";
+    throw ex;
+  }
+
+  VOLID v;
+  strncpy(v.external_label, str.c_str(), sizeof(v.external_label));
+  v.external_label[sizeof(v.external_label) - 1] = '\0';
+  return v;
+}
+
+//------------------------------------------------------------------------------
+// onlyContainsNumerals
+//------------------------------------------------------------------------------
+bool castor::tape::rmc::Acs::onlyContainsNumerals(
+  const std::string &str) const throw() {
+  for(std::string::const_iterator itor = str.begin(); itor != str.end();
+    itor++) {
+    if(*itor < '0' || *itor  > '9') {
+      return false;
+    }
+  }
+  return true;
+}
+
+//------------------------------------------------------------------------------
+// driveId2Str
+//------------------------------------------------------------------------------
+std::string castor::tape::rmc::Acs::driveId2Str(const DRIVEID &driveId)
+  const throw() {
+  std::ostringstream oss;
+  oss << std::setfill('0') <<
+    std::setw(3) << (int32_t)driveId.panel_id.lsm_id.acs << ":" <<
+    std::setw(3) << (int32_t)driveId.panel_id.lsm_id.lsm << ":" <<
+    std::setw(3) << (int32_t)driveId.panel_id.panel << ":" <<
+    std::setw(3) << (int32_t)driveId.drive;
+  return oss.str();
+}
diff --git a/castor/tape/rmc/Acs.hpp b/castor/tape/rmc/Acs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6579b88a953311edfad9d919b39c0b3ea349fe09
--- /dev/null
+++ b/castor/tape/rmc/Acs.hpp
@@ -0,0 +1,177 @@
+/******************************************************************************
+ *                 castor/tape/rmc/Acs.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACS_HPP
+#define CASTOR_TAPE_RMC_ACS_HPP 1
+
+#include "castor/exception/InvalidArgument.hpp"
+
+extern "C" {
+#include "acssys.h"
+#include "acsapi.h"
+}
+
+#include <string>
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * Abstract class that defines the interface to an object that wraps the ACLS
+ * C-API.
+ */
+class Acs {
+public:
+  /**
+   * Pure-virtual destructor to ensure this class is abstract.
+   */
+  virtual ~Acs() throw() = 0;
+
+  /**
+   * Parses the specified string and returns the corresponding drive ID object.
+   *
+   * This method throws a castor::exception::InvalidArgument if the syntax of
+   * the string is invalid.
+   *
+   * @param str The string to be parsed.
+   * @return The drive ID object.
+   */
+  DRIVEID str2DriveId(const std::string &str) const
+    throw(castor::exception::InvalidArgument);
+
+  /**
+   * Returns the VOLID equibvalent of the specified string.
+   *
+   * This method throws a castor::exception::InvalidArgument if the string is
+   * longer than EXTERNAL_LABEL_SIZE.
+   *
+   * @param str The string representation of the volume identifier.
+   * @return The VOLID representation of the volume identifier.
+   */
+  VOLID str2Volid(const std::string &str) const
+    throw(castor::exception::InvalidArgument);
+
+  /**
+   * Returns true if the specified string only contains numerals else false.
+   *
+   * @return True if the specified string only contains numerals else false.
+   */
+  bool onlyContainsNumerals(const std::string &str) const throw();
+
+  /**
+   * Returns the string reprsentation of the specified drive identifier.
+   *
+   * The string format is ACS:LSM:panel:drive
+   *
+   * @param driveId The drive identifier.
+   * @return The string representation.
+   */
+  std::string driveId2Str(const DRIVEID &driveId) const throw();
+
+  /**
+   * C++ wrapper around the acs_mount() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param lockId Lock identifier or 0 meaning no lock.
+   * @param volId The indentifier of volume to be mounted.
+   * @param driveId The ID of the drive into which the volume is to be mounted.
+   * @param readOnly Set to true to request the volume be mounted for read-only
+   * access.
+   * @param bypass Set to true to override the ACSLS verification of
+   * compatibility between the drive and the media type of the volume.
+   * @return status value returned by acs_mount().
+   */
+  virtual STATUS mount(
+    const SEQ_NO seqNumber,
+    const LOCKID lockId,
+    const VOLID &volId,
+    const DRIVEID &driveId,
+    const BOOLEAN readOnly,
+    const BOOLEAN bypass)
+    throw() = 0;
+
+  /**
+   * C++ wrapper around the acs_dismount() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param lockId Lock identifier or 0 meaning no lock.
+   * @param volId The identifier of the volume to be mounted.
+   * @param driveId The ID of the drive into which the volume is to be mounted.
+   * @param force Set to true if the dismount should be forced.  Forcing a
+   * dismount means dismounting the volume from the specified drive without
+   * checking the identifier of the volume.
+   * @return status value returned by acs_dismount().
+   */
+  virtual STATUS dismount(
+    const SEQ_NO seqNumber,
+    const LOCKID lockId,
+    const VOLID &volId,
+    const DRIVEID &driveId,
+    const BOOLEAN force)
+    throw() = 0;
+
+  /**
+   * C++ wrapper around the acs_response() function of the ACSLS C-API.
+   *
+   * @param timeout Time in seconds to wait for a response.  A value of -1
+   * means block indefinitely and an a value of 0 means poll for the existence
+   * of a response.
+   * @param seqNumber Output parameter.  If a response exists then seqNumber
+   * is set.
+   * @param reqId Output parameter.  For an acknowledge response reqId is set
+   * to the request identifier of the original request. For an intermediate or
+   * final response reqId will be set to 0.
+   * @param rType Output parameter.  Set to the type of the response.
+   * @param rBuf Output parameter.  Set to the response information.
+   * @return status value returned by acs_response().
+   */
+  virtual STATUS response(
+    const int timeout,
+    SEQ_NO &seqNumber,
+    REQ_ID &reqId,
+    ACS_RESPONSE_TYPE &rType,
+    ALIGNED_BYTES rBuf) throw() = 0;
+
+  /**
+   * C++ wrapper around the acs_query_volume() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param volIds Array of the volume identifiers to be queried.
+   * @param count The number of volume identifiers contained iwthin the volId
+   * parameter.
+   * @return status value returned by acs_response().
+   */
+  virtual STATUS queryVolume(
+    const SEQ_NO seqNumber,
+    VOLID (&volIds)[MAX_ID],
+    const unsigned short count) throw() = 0;
+}; // class  Acs
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_ACS_HPP
diff --git a/castor/tape/rmc/AcsCmd.cpp b/castor/tape/rmc/AcsCmd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c70d9f64aa1eb3797a29fb358ed147fb71bb9c5a
--- /dev/null
+++ b/castor/tape/rmc/AcsCmd.cpp
@@ -0,0 +1,143 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsCmd.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsCmd.hpp"
+#include "castor/tape/utils/utils.hpp"
+
+#include <stdlib.h>
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsCmd::AcsCmd(std::istream &inStream,
+  std::ostream &outStream, std::ostream &errStream, Acs &acs) throw():
+  m_in(inStream), m_out(outStream), m_err(errStream), m_acs(acs),
+  m_debugBuf(outStream), m_dbg(&m_debugBuf) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsCmd::~AcsCmd() throw() {
+}
+
+//------------------------------------------------------------------------------
+// bool2Str
+//------------------------------------------------------------------------------
+std::string castor::tape::rmc::AcsCmd::bool2Str(bool &value) const
+  throw() {
+  if(value) {
+    return "TRUE";
+  } else {
+    return "FALSE";
+  }
+}
+
+//------------------------------------------------------------------------------
+// bool2Str
+//------------------------------------------------------------------------------
+std::string castor::tape::rmc::AcsCmd::bool2Str(BOOLEAN &value) const
+  throw() {
+  if(value) {
+    return "TRUE";
+  } else {
+    return "FALSE";
+  }
+}
+
+//------------------------------------------------------------------------------
+// requestResponsesUntilFinal
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsCmd::requestResponsesUntilFinal(
+  const SEQ_NO requestSeqNumber,
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)],
+  const int queryInterval, const int timeout)
+  throw (castor::exception::RequestFailed) {
+  ACS_RESPONSE_TYPE responseType = RT_NONE;
+  int elapsedTime = 0;
+  do {
+    const int remainingTime = timeout - elapsedTime;
+    const int responseTimeout = remainingTime > queryInterval ?
+      queryInterval : remainingTime;
+
+    const time_t startTime = time(NULL);
+    responseType = requestResponse(responseTimeout, requestSeqNumber, buf);
+    elapsedTime += time(NULL) - startTime;
+
+    if(RT_ACKNOWLEDGE == responseType) {
+      m_dbg << "Received RT_ACKNOWLEDGE" << std::endl;
+    }
+
+    if(elapsedTime >= timeout) {
+      castor::exception::RequestFailed ex;
+      ex.getMessage() << "Timed out after " << timeout << " seconds";
+      throw(ex);
+    }
+  } while(RT_FINAL != responseType);
+
+  m_dbg << "Received RT_FINAL" << std::endl;
+}
+
+//------------------------------------------------------------------------------
+// requestResponse
+//------------------------------------------------------------------------------
+ACS_RESPONSE_TYPE castor::tape::rmc::AcsCmd::requestResponse(
+  const int timeout, const SEQ_NO requestSeqNumber,
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+  throw(castor::exception::RequestFailed) {
+  SEQ_NO responseSeqNumber = 0;
+  REQ_ID reqId = (REQ_ID)0;
+  ACS_RESPONSE_TYPE responseType = RT_NONE;
+
+  m_dbg << "Calling Acs::response()" << std::endl;
+  const STATUS s = m_acs.response(timeout, responseSeqNumber, reqId,
+    responseType, buf);
+  m_dbg << "Acs::response() returned " << acs_status(s) << std::endl;
+
+  switch(s) {
+  case STATUS_SUCCESS:
+    checkResponseSeqNumber(requestSeqNumber, responseSeqNumber);
+    return responseType;
+  case STATUS_PENDING:
+    return RT_NONE;
+  default:
+    castor::exception::RequestFailed ex;
+    ex.getMessage() << "Failed to request response: " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// checkSeqNumber
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsCmd::checkResponseSeqNumber(
+  const SEQ_NO requestSeqNumber, const SEQ_NO responseSeqNumber)
+  throw(castor::exception::Mismatch) {
+  if(requestSeqNumber != responseSeqNumber) {
+    castor::exception::Mismatch ex;
+    ex.getMessage() <<  ": Sequence number mismatch: requestSeqNumber="
+      << requestSeqNumber << " responseSeqNumber=" << responseSeqNumber;
+    throw(ex);
+  }
+}
diff --git a/castor/tape/rmc/AcsCmd.hpp b/castor/tape/rmc/AcsCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0dfceeba22fc9e1ffcfa337c7dc20a84c39f4e07
--- /dev/null
+++ b/castor/tape/rmc/AcsCmd.hpp
@@ -0,0 +1,165 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSCMD_HPP
+#define CASTOR_TAPE_RMC_ACSCMD_HPP 1
+
+#include "castor/exception/InvalidArgument.hpp"
+#include "castor/exception/Mismatch.hpp"
+#include "castor/exception/RequestFailed.hpp"
+#include "castor/tape/rmc/Acs.hpp"
+#include "castor/tape/rmc/DebugBuf.hpp"
+
+#include <istream>
+#include <ostream>
+#include <string>
+
+extern "C" {
+#include "acssys.h"
+#include "acsapi.h"
+}
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * Abstract class implementing common code and data structures for command-line
+ * tools that interact with ACLS compatible tape libraries.
+ */
+class AcsCmd {
+public:
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  AcsCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw();
+
+  /**
+   * Pure-virtual destructor to guarantee this class is abstract.
+   */
+  virtual ~AcsCmd() throw() = 0;
+
+protected:
+
+  /**
+   * Standard input stream.
+   */
+  std::istream &m_in;
+
+  /**
+   * Standard output stream.
+   */
+  std::ostream &m_out;
+
+  /**
+   * Standard error stream.
+   */
+  std::ostream &m_err;
+
+  /**
+   * Wrapper around the ACSLS C-API.
+   */
+  Acs &m_acs;
+
+  /**
+   * Debug stream buffer that inserts a standard debug preamble before each
+   * message-line written to it.
+   */
+  DebugBuf m_debugBuf;
+
+  /**
+   * Stream used to write debug messages.
+   *
+   * This stream will insert a standard debug preamble before each message-line
+   * written to it.
+   */
+  std::ostream m_dbg;
+
+  /**
+   * Returns the string representation of the specfied boolean value.
+   */
+  std::string bool2Str(bool &value) const throw();
+
+  /**
+   * Returns the string representation of the specfied boolean value.
+   */
+  std::string bool2Str(BOOLEAN &value) const throw();
+
+  /**
+   * Requests responses from ACSLS in a loop until the RT_FINAL response is
+   * received.
+   *
+   * @param requestSeqNumber The sequemce number that was sent in the initial
+   * request to the ACSLS.
+   * @param buf Output parameter.  Message buffer into which the RT_FINAL
+   * response shall be written.
+   * @param queryInterval Time in seconds to wait between queries to ACS for
+   * responses.
+   * @param timeout The time in seconds to spend trying to get the RT_FINAL
+   * response.
+   */
+  void requestResponsesUntilFinal(const SEQ_NO requestSeqNumber,
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)],
+    const int queryInterval, const int timeout)
+    throw (castor::exception::RequestFailed);
+
+  /**
+   * Sends a request for a response to the ACSLS.
+   *
+   * @param timeout The timeout.
+   * @param requestSeqNumber The sequemce number that was sent in the initial
+   * request to the ACSLS.
+   * @param buf Output parameter.  The response message if there is one.
+   * @return The type of the response message if there is one or RT_NONE if
+   * there isn't one.
+   */
+  ACS_RESPONSE_TYPE requestResponse(const int timeout,
+    const SEQ_NO requestSeqNumber,
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+    throw(castor::exception::RequestFailed);
+
+  /**
+   * Throws castor::exception::Mismatch if the specified request and
+   * response sequence-numbers do not match.
+   *
+   * @param requestSeqNumber Request sequence-number.
+   * @param responseSeqNumber Response sequence-number.
+   */
+  void checkResponseSeqNumber(const SEQ_NO requestSeqNumber,
+    const SEQ_NO responseSeqNumber) throw(castor::exception::Mismatch);
+
+}; // class AcsCmd
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_ACSCMD_HPP
diff --git a/castor/tape/rmc/AcsDismountCmd.cpp b/castor/tape/rmc/AcsDismountCmd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..07f7487c71c25755fd21ec65b5f8eef516659255
--- /dev/null
+++ b/castor/tape/rmc/AcsDismountCmd.cpp
@@ -0,0 +1,305 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsDismountCmd.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsDismountCmd.hpp"
+#include "castor/tape/utils/utils.hpp"
+
+#include <getopt.h>
+ 
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsDismountCmd::AcsDismountCmd(
+  std::istream &inStream, std::ostream &outStream, std::ostream &errStream,
+  Acs &acs) throw():
+  AcsCmd(inStream, outStream, errStream, acs), m_defaultQueryInterval(10),
+  m_defaultTimeout(600) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsDismountCmd::~AcsDismountCmd() throw() {
+  // Do nothing
+}
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int castor::tape::rmc::AcsDismountCmd::main(const int argc,
+  char *const *const argv) throw() {
+  try {
+    m_cmdLine = parseCmdLine(argc, argv);
+  } catch(castor::exception::InvalidArgument &ia) {
+    m_err << "Aborting: Invalid command-line: " << ia.getMessage().str() <<
+      std::endl;
+    m_err << std::endl;
+    usage(m_err);
+    return 1;
+  } catch(castor::exception::MissingOperand &mo) {
+    m_err << "Aborting: Missing operand: " << mo.getMessage().str() <<
+      std::endl;
+    m_err << std::endl;
+    usage(m_err);
+    return 1;
+  } catch(castor::exception::Internal &ie) {
+    m_err << "Aborting: Internal error: " << ie.getMessage().str() <<
+      std::endl;
+    return 1;
+  }
+
+  // Display the usage message to standard out and exit with success if the
+  // user requested help
+  if(m_cmdLine.help) {
+    usage(m_out);
+    return 0;
+  }
+
+  // Setup debug mode to be on or off depending on the command-line arguments
+  m_debugBuf.setDebug(m_cmdLine.debug);
+
+  m_dbg << "force = " << (m_cmdLine.force ? "TRUE" : "FALSE") << std::endl;
+  m_dbg << "query = " << m_cmdLine.queryInterval << std::endl;
+  m_dbg << "timeout = " << m_cmdLine.timeout << std::endl;
+  m_dbg << "VID = " << m_cmdLine.volId.external_label << std::endl;
+  m_dbg << "DRIVE = " << m_acs.driveId2Str(m_cmdLine.driveId) << std::endl;
+
+  try {
+    syncDismount();
+  } catch(castor::exception::Exception &ex) {
+    m_err << "Aborting: " << ex.getMessage().str() << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+//------------------------------------------------------------------------------
+// usage
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsDismountCmd::usage(std::ostream &os)
+  const throw() {
+  os <<
+  "Usage:\n"
+  "  dismountacs [options] VID DRIVE\n"
+  "\n"
+  "Where:\n"
+  "\n"
+  "  VID    The VID of the volume to be dismounted.\n"
+  "  DRIVE  The drive from which the volume is to be dismounted.\n"
+  "\n"
+  "Options:\n"
+  "\n"
+  "  -d|--debug            Turn on the printing of debug information.\n"
+  "  -f|--force            Force the dismount.\n"
+  "  -h|--help             Print this help message and exit.\n"
+  "  -q|--query SECONDS    Time to wait between queries to ACS for responses.\n"
+  "                        SECONDS must be an integer value greater than 0.\n"
+  "                        The default value of SECONDS is "
+    << m_defaultQueryInterval << ".\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 "
+    << m_defaultTimeout << ".\n"
+  "\n"
+  "Comments to: Castor.Support@cern.ch" << std::endl;
+}
+
+//------------------------------------------------------------------------------
+// parseCmdLine
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsDismountCmdLine
+  castor::tape::rmc::AcsDismountCmd::parseCmdLine(
+  const int argc, char *const *const argv)
+  throw(castor::exception::Internal, castor::exception::InvalidArgument,
+    castor::exception::MissingOperand) {
+
+  static struct option longopts[] = {
+    {"debug", 0, NULL, 'd'},
+    {"force", 0, NULL, 'f'},
+    {"help" , 0, NULL, 'h'},
+    {"query" , required_argument, NULL, 'q'},
+    {"timeout" , required_argument, NULL, 't'},
+    {NULL   , 0, NULL,  0 }
+  };
+  AcsDismountCmdLine cmdLine;
+  char c;
+
+  // Set the query option to the default value
+  cmdLine.queryInterval = m_defaultQueryInterval;
+
+  // Set timeout option to the default value
+  cmdLine.timeout = m_defaultTimeout;
+
+  // Prevent getopt() from printing an error message if it does not recognize
+  // an option character
+  opterr = 0;
+  while((c = getopt_long(argc, argv, ":dfhq:t:", longopts, NULL)) != -1) {
+
+    switch (c) {
+    case 'd':
+      cmdLine.debug = true;
+      break;
+    case 'f':
+      cmdLine.force = TRUE;
+      break;
+    case 'h':
+      cmdLine.help = true;
+      break;
+    case 'q':
+      cmdLine.queryInterval = atoi(optarg);
+      if(0 >= cmdLine.queryInterval) {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "Query value must be an integer greater than 0"
+          ": value=" << cmdLine.queryInterval;
+        throw ex;
+      }
+      break;
+    case 't':
+      cmdLine.timeout = atoi(optarg);
+      if(0 >= cmdLine.timeout) {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "Timeout value must be an integer greater than 0"
+          ": value=" << cmdLine.timeout;
+        throw ex;
+      }
+      break;
+    case ':':
+      {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "The -" << (char)optopt
+          << " option requires a parameter";
+        throw ex;
+      }
+      break;
+    case '?':
+      {
+        castor::exception::InvalidArgument ex;
+
+        if(optopt == 0) {
+          ex.getMessage() << "Unknown command-line option";
+        } else {
+          ex.getMessage() << "Unknown command-line option: -" << (char)optopt;
+        }
+        throw ex;
+      }
+      break;
+    default:
+      {
+        castor::exception::Internal ex;
+        ex.getMessage() <<
+          "getopt_long returned the following unknown value: 0x" <<
+          std::hex << (int)c;
+        throw ex;
+      }
+    } // switch (c)
+  } // while ((c = getopt_long( ... )) != -1)
+
+  // There is no need to continue parsing when the help option is set
+  if(cmdLine.help) {
+    return cmdLine;
+  }
+
+  // Calculate the number of non-option ARGV-elements
+  const int nbArgs = argc-optind;
+
+  // Check that both VID and DRIVE has been specified
+  if(nbArgs < 2) {
+    castor::exception::MissingOperand ex;
+
+    ex.getMessage() <<
+      "Both VID and DRIVE must be specified";
+
+    throw ex;
+  }
+
+  // Parse the volume identifier of the command-line argument
+  cmdLine.volId = m_acs.str2Volid(argv[optind]);
+
+  // Move on to the next command-line argument
+  optind++;
+
+  // Parse the drive-identifier command-line argument
+  cmdLine.driveId = m_acs.str2DriveId(argv[optind]);
+
+  return cmdLine;
+}
+
+//------------------------------------------------------------------------------
+// syncDismount
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsDismountCmd::syncDismount()
+  throw(castor::exception::DismountFailed) {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendDismountRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, m_cmdLine.queryInterval,
+      m_cmdLine.timeout);
+    processDismountResponse(buf);
+  } catch(castor::exception::Exception &ex) {
+    castor::exception::DismountFailed df;
+    df.getMessage() << "Failed to dismount volume " <<
+      m_cmdLine.volId.external_label << ": " << ex.getMessage().str();
+    throw df;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendDismountRequest
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsDismountCmd::sendDismountRequest(
+  const SEQ_NO seqNumber) throw (castor::exception::DismountFailed) {
+  const LOCKID lockId = 0; // No lock
+
+  m_dbg << "Calling Acs::dismount()" << std::endl;
+  const STATUS s = m_acs.dismount(seqNumber, lockId, m_cmdLine.volId,
+    m_cmdLine.driveId, m_cmdLine.force);
+  m_dbg << "Acs::dismount() returned " << acs_status(s) << std::endl;
+  if(STATUS_SUCCESS != s) {
+    castor::exception::DismountFailed ex;
+    ex.getMessage() << "Failed to send request to dismount volume " <<
+      m_cmdLine.volId.external_label << " from drive " <<
+      m_acs.driveId2Str(m_cmdLine.driveId) << ": force=" <<
+      (m_cmdLine.force ? "TRUE" : "FALSE") << ": " << acs_status(s);
+    throw(ex);
+  }
+}
+
+//------------------------------------------------------------------------------
+// processDismountResponse
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsDismountCmd::processDismountResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+  throw(castor::exception::DismountFailed) {
+  const ACS_DISMOUNT_RESPONSE *const msg = (ACS_DISMOUNT_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->dismount_status) {
+    castor::exception::DismountFailed ex;
+    ex.getMessage() << "Status of dismount response is not success: " <<
+      acs_status(msg->dismount_status);
+    throw(ex);
+  }
+}
diff --git a/castor/tape/rmc/AcsDismountCmd.hpp b/castor/tape/rmc/AcsDismountCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..02cfe6dc2c932124c1959f2f3a8a30143d8f8be4
--- /dev/null
+++ b/castor/tape/rmc/AcsDismountCmd.hpp
@@ -0,0 +1,150 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsDismountCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSDISMOUNTCMD_HPP
+#define CASTOR_TAPE_RMC_ACSDISMOUNTCMD_HPP 1
+
+#include "castor/exception/DismountFailed.hpp"
+#include "castor/exception/Internal.hpp"
+#include "castor/exception/InvalidArgument.hpp"
+#include "castor/exception/MissingOperand.hpp"
+#include "castor/tape/rmc/AcsCmd.hpp"
+#include "castor/tape/rmc/AcsDismountCmdLine.hpp"
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * The class implementing the mount command.
+ */
+class AcsDismountCmd: public AcsCmd {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  AcsDismountCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw();
+
+  /**
+   * Destructor.
+   */
+  virtual ~AcsDismountCmd() throw();
+
+  /**
+   * The entry function of the command.
+   *
+   * This method sets the m_cmdLine member-variable.
+   *
+   * @param argc The number of command-line arguments.
+   * @param argv The command-line arguments.
+   */
+  int main(const int argc, char *const *const argv) throw();
+
+protected:
+
+  /**
+   * Writes the command-line usage message of to the specified output stream.
+   *
+   * @param os Output stream to be written to.
+   */
+  void usage(std::ostream &os) const throw();
+
+  /**
+   * Parses the specified command-line arguments.
+   *
+   * @param argc Argument count from the executable's entry function: main().
+   * @param argv Argument vector from the executable's entry function: main().
+   * @return The parsed command-line.
+   */
+  AcsDismountCmdLine parseCmdLine(const int argc, char *const *const argv)
+    throw(castor::exception::Internal, castor::exception::InvalidArgument,
+      castor::exception::MissingOperand);
+
+  /**
+   * Dismounts the tape with the specified VID into the drive with the specified
+   * drive ID.
+   *
+   * This method does not return until the mount has either suceeded, failed or
+   * the specified timeout has been reached.
+   *
+   * @param dismountTimeout The maximum amount of time in seconds to wait for
+   * the dismount operation to conclude.
+   * @param queryInterval The amount of time in seconds to wait between
+   * querying ACS for responses.
+   */
+  void syncDismount() throw(castor::exception::DismountFailed);
+
+  /**
+   * Sends the dismount request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendDismountRequest(const SEQ_NO seqNumber)
+    throw(castor::exception::DismountFailed);
+
+  /**
+   * Throws castor::exception::DismountFailed if the mount was not
+   * successful.
+   *
+   * @param buf The mount-response message.
+   */
+  void processDismountResponse(
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+    throw(castor::exception::DismountFailed);
+
+private:
+
+  /**
+   * The parsed command-line.
+   *
+   * This member-variable is set by the main() method of this class.
+   */
+  AcsDismountCmdLine m_cmdLine;
+
+  /**
+   * The default time in seconds to wait between queries to ACS for responses.
+   */
+  const int m_defaultQueryInterval;
+
+  /**
+   * The default timeout value in seconds for the dismount to conclude either
+   * success or failure.
+   */
+  const int m_defaultTimeout;
+
+}; // class AcsDismountCmd
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_ACSDISMOUNTCMD_HPP
diff --git a/castor/tape/rmc/AcsDismountCmdLine.cpp b/castor/tape/rmc/AcsDismountCmdLine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a5b6e30a428941bf938f8f5b22fca0503c58496
--- /dev/null
+++ b/castor/tape/rmc/AcsDismountCmdLine.cpp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsDismountCmdLine.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsDismountCmdLine.hpp"
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+castor::tape::rmc::AcsDismountCmdLine::AcsDismountCmdLine() throw():
+  debug(false),
+  force(FALSE),
+  help(false),
+  queryInterval(0),
+  timeout(0) {
+  driveId.panel_id.lsm_id.acs = (ACS)0;
+  driveId.panel_id.lsm_id.lsm = (LSM)0;
+  driveId.panel_id.panel = (PANEL)0;
+  driveId.drive = (DRIVE)0;
+  memset(volId.external_label, '\0', sizeof(volId.external_label));
+}
diff --git a/castor/tape/rmc/AcsDismountCmdLine.hpp b/castor/tape/rmc/AcsDismountCmdLine.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c1a58174037914b5ff9d429f6b93aae4b096eaa
--- /dev/null
+++ b/castor/tape/rmc/AcsDismountCmdLine.hpp
@@ -0,0 +1,96 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsDismountCmdLine.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSDISMOUNTCMDLINE_HPP
+#define CASTOR_TAPE_RMC_ACSDISMOUNTCMDLINE_HPP 1
+
+extern "C" {
+#include "acssys.h"
+#include "acsapi.h"
+}
+
+#include <string>
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * Data type used to store the results of parsing the command-line.
+ */
+struct AcsDismountCmdLine {
+  /**
+   * Constructor.
+   *
+   * Initialises all BOOLEAN member-variables to FALSE, all integer
+   * member-variables to 0 and the volume identifier to an empty string.
+   */
+  AcsDismountCmdLine() throw();
+
+  /**
+   * True if the debug option has been set.
+   */
+  bool debug;
+
+  /**
+   * True if the dismount should be forced.
+   *
+   * Forcing a dismount means dismounting the tape in the specified drive
+   * without checking the volume identifier of the tape.
+   */
+  BOOLEAN force;
+
+  /**
+   * True if the help option has been set.
+   */
+  bool help;
+
+  /**
+   * Time in seconds to wait between queries to ACS for responses.
+   */
+  int queryInterval;
+
+  /**
+   * Time in seconds to wait for the dismount to conclude.
+   */
+  int timeout;
+
+  /**
+   * The volume identifier of the tape to be mounted.
+   */
+  VOLID volId;
+
+  /**
+   * The drive into which the tape is to be mounted.
+   */
+  DRIVEID driveId;
+
+}; // class AcsDismountCmdLine
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_ACSDISMOUNTCMDLINE_HPP
diff --git a/castor/tape/rmc/AcsDismountMain.cpp b/castor/tape/rmc/AcsDismountMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f93dfa9c8e1c1b442faf627eaedde1e33587e4d1
--- /dev/null
+++ b/castor/tape/rmc/AcsDismountMain.cpp
@@ -0,0 +1,39 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsDismountMain.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+ 
+#include "castor/tape/rmc/AcsImpl.hpp"
+#include "castor/tape/rmc/AcsDismountCmd.hpp"
+
+#include <iostream>
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int main(const int argc, char *const *const argv) {
+  castor::tape::rmc::AcsImpl acs;
+  castor::tape::rmc::AcsDismountCmd
+    cmd(std::cin, std::cout, std::cerr, acs);
+
+  return cmd.main(argc, argv);
+}
diff --git a/castor/tape/rmc/AcsImpl.cpp b/castor/tape/rmc/AcsImpl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..73ff227865f3240680b8ce83238c9ae0c406acca
--- /dev/null
+++ b/castor/tape/rmc/AcsImpl.cpp
@@ -0,0 +1,84 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsImpl.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsImpl.hpp"
+
+#include <errno.h>
+#include <sstream>
+#include <string.h>
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsImpl::~AcsImpl() throw() {
+}
+
+//------------------------------------------------------------------------------
+// mount
+//------------------------------------------------------------------------------
+STATUS castor::tape::rmc::AcsImpl::mount(
+  const SEQ_NO seqNumber,
+  const LOCKID lockId,
+  const VOLID &volId,
+  const DRIVEID &driveId,
+  const BOOLEAN readOnly,
+  const BOOLEAN bypass)
+  throw() {
+  return acs_mount(seqNumber, lockId, volId, driveId, readOnly, bypass);
+}
+
+//------------------------------------------------------------------------------
+// dismount
+//------------------------------------------------------------------------------
+STATUS castor::tape::rmc::AcsImpl::dismount(
+  const SEQ_NO seqNumber,
+  const LOCKID lockId,
+  const VOLID &volId,
+  const DRIVEID &driveId,
+  const BOOLEAN force)
+  throw() {
+  return acs_dismount(seqNumber, lockId, volId, driveId, force);
+}
+
+//------------------------------------------------------------------------------
+// response
+//------------------------------------------------------------------------------
+STATUS castor::tape::rmc::AcsImpl::response(
+  const int timeout,
+  SEQ_NO &seqNumber,
+  REQ_ID &reqId,
+  ACS_RESPONSE_TYPE &rType,
+  ALIGNED_BYTES rBuf) throw() {
+  return acs_response(timeout, &seqNumber, &reqId, &rType, rBuf);
+}
+
+//------------------------------------------------------------------------------
+// queryVolume
+//------------------------------------------------------------------------------
+STATUS castor::tape::rmc::AcsImpl::queryVolume(
+  const SEQ_NO seqNumber,
+  VOLID (&volIds)[MAX_ID],
+  const unsigned short count) throw() {
+  return acs_query_volume(seqNumber, volIds, count);
+}
diff --git a/castor/tape/rmc/AcsImpl.hpp b/castor/tape/rmc/AcsImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b2824669e581e32851dda4c1b99ab5af5c43fd8
--- /dev/null
+++ b/castor/tape/rmc/AcsImpl.hpp
@@ -0,0 +1,129 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsImpl.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSIMPL_HPP
+#define CASTOR_TAPE_RMC_ACSIMPL_HPP 1
+
+#include "castor/tape/rmc/Acs.hpp"
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * Concrete class that wraps the ACLS C-API.
+ */
+class AcsImpl: public Acs {
+public:
+  /**
+   * Destructor.
+   */
+  ~AcsImpl() throw();
+
+  /**
+   * C++ wrapper around the acs_mount() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param lockId Lock identifier or 0 meaning no lock.
+   * @param volId The identifier of the volume to be mounted.
+   * @param driveId The ID of the drive into which the volume is to be mounted.
+   * @param readOnly Set to true to request the volume be mounted for read-only
+   * access.
+   * @param bypass Set to true to override the ACSLS verification of
+   * compatibility between the drive and the media type of the volume.
+   * @return status value returned by acs_mount().
+   */
+  STATUS mount(
+    const SEQ_NO seqNumber,
+    const LOCKID lockId,
+    const VOLID &volId,
+    const DRIVEID &driveId,
+    const BOOLEAN readOnly,
+    const BOOLEAN bypass)
+    throw();
+
+  /**
+   * C++ wrapper around the acs_dismount() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param lockId Lock identifier or 0 meaning no lock.
+   * @param volId The identifier of the volume to be mounted.
+   * @param driveId The ID of the drive into which the volume is to be mounted.
+   * @param force Set to true if the dismount should be forced.  Forcing a
+   * dismount means dismounting the volume from the specified drive without
+   * checking the identifier of the volume.
+   * @return status value returned by acs_dismount().
+   */
+  STATUS dismount(
+    const SEQ_NO seqNumber,
+    const LOCKID lockId,
+    const VOLID &volId,
+    const DRIVEID &driveId,
+    const BOOLEAN force)
+    throw();
+
+  /**
+   * C++ wrapper around the acs_response() function of the ACSLS C-API.
+   *
+   * @param timeout Time in seconds to wait for a response.  A value of -1
+   * means block indefinitely and an a value of 0 means poll for the existence
+   * of a response.
+   * @param seqNumber Output parameter.  If a response exists then seqNumber
+   * is set.
+   * @param reqId Output parameter.  For an acknowledge response reqId is set
+   * to the request identifier of the original request. For an intermediate or
+   * final response reqId will be set to 0.
+   * @param rType Output parameter.  Set to the type of the response.
+   * @param rBuf Output parameter.  Set to the response information.
+   * @return status value returned by acs_response().
+   */
+  STATUS response(
+    const int timeout,
+    SEQ_NO &seqNumber,
+    REQ_ID &reqId,
+    ACS_RESPONSE_TYPE &rType,
+    ALIGNED_BYTES rBuf) throw();
+
+  /**
+   * C++ wrapper around the acs_query_volume() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param volIds Array of the volume identifiers to be queried.
+   * @param count The number of volume identifiers contained iwthin the volId
+   * parameter.
+   * @return status value returned by acs_response().
+   */
+  STATUS queryVolume(
+    const SEQ_NO seqNumber,
+    VOLID (&volIds)[MAX_ID],
+    const unsigned short count) throw();
+
+}; // class AcsImpl
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_ACSIMPL_HPP
diff --git a/castor/tape/rmc/AcsMountCmd.cpp b/castor/tape/rmc/AcsMountCmd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..62589bc05eb052c890f83049e9ad36788818dd9f
--- /dev/null
+++ b/castor/tape/rmc/AcsMountCmd.cpp
@@ -0,0 +1,306 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsMountCmd.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsMountCmd.hpp"
+#include "castor/tape/utils/utils.hpp"
+
+#include <getopt.h>
+#include <iostream>
+ 
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsMountCmd::AcsMountCmd(
+  std::istream &inStream, std::ostream &outStream, std::ostream &errStream,
+  Acs &acs) throw():
+  AcsCmd(inStream, outStream, errStream, acs), m_defaultQueryInterval(10),
+  m_defaultTimeout(600) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsMountCmd::~AcsMountCmd() throw() {
+  // Do nothing
+}
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int castor::tape::rmc::AcsMountCmd::main(const int argc,
+  char *const *const argv) throw() {
+  try {
+    m_cmdLine = parseCmdLine(argc, argv);
+  } catch(castor::exception::InvalidArgument &ia) {
+    m_err << "Aborting: Invalid command-line: " << ia.getMessage().str() <<
+      std::endl;
+    m_err << std::endl;
+    usage(m_err);
+    return 1;
+  } catch(castor::exception::MissingOperand &mo) {
+    m_err << "Aborting: Missing operand: " << mo.getMessage().str() <<
+      std::endl;
+    m_err << std::endl;
+    usage(m_err);
+    return 1;
+  } catch(castor::exception::Internal &ie) {
+    m_err << "Aborting: Internal error: " << ie.getMessage().str() <<
+      std::endl;
+    return 1;
+  }
+
+  // Display the usage message to standard out and exit with success if the
+  // user requested help
+  if(m_cmdLine.help) {
+    usage(m_out);
+    return 0;
+  }
+
+  // Setup debug mode to be on or off depending on the command-line arguments
+  m_debugBuf.setDebug(m_cmdLine.debug);
+
+  m_dbg << "query = " << m_cmdLine.queryInterval << std::endl;
+  m_dbg << "readonly = " << bool2Str(m_cmdLine.readOnly) << std::endl;
+  m_dbg << "timeout = " << m_cmdLine.timeout << std::endl;
+  m_dbg << "VID = " << m_cmdLine.volId.external_label << std::endl;
+  m_dbg << "DRIVE = " << m_acs.driveId2Str(m_cmdLine.driveId) << std::endl;
+
+  try {
+    syncMount();
+  } catch(castor::exception::Exception &ex) {
+    m_err << "Aborting: " << ex.getMessage().str() << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+//------------------------------------------------------------------------------
+// parseCmdLine
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsMountCmdLine
+  castor::tape::rmc::AcsMountCmd::parseCmdLine(
+  const int argc, char *const *const argv)
+  throw(castor::exception::Internal, castor::exception::InvalidArgument,
+    castor::exception::MissingOperand) {
+
+  static struct option longopts[] = {
+    {"debug", 0, NULL, 'd'},
+    {"help" , 0, NULL, 'h'},
+    {"query" , required_argument, NULL, 'q'},
+    {"readonly" , 0, NULL, 'r'},
+    {"timeout" , required_argument, NULL, 't'},
+    {NULL, 0, NULL, 0}
+  };
+  AcsMountCmdLine cmdLine;
+  char c;
+
+  // Set the query option to the default value
+  cmdLine.queryInterval = m_defaultQueryInterval;
+
+  // Set timeout option to the default value
+  cmdLine.timeout = m_defaultTimeout;
+
+  // Prevent getopt() from printing an error message if it does not recognize
+  // an option character
+  opterr = 0;
+  while((c = getopt_long(argc, argv, ":dhq:rt:", longopts, NULL)) != -1) {
+
+    switch (c) {
+    case 'd':
+      cmdLine.debug = true;
+      break;
+    case 'h':
+      cmdLine.help = true;
+      break;
+    case 'q':
+      cmdLine.queryInterval = atoi(optarg);
+      if(0 >= cmdLine.queryInterval) {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "Query value must be an integer greater than 0"
+          ": value=" << cmdLine.queryInterval;
+        throw ex;
+      }
+      break;
+    case 'r':
+      cmdLine.readOnly = TRUE;
+      break;
+    case 't':
+      cmdLine.timeout = atoi(optarg);
+      if(0 >= cmdLine.timeout) {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "Timeout value must be an integer greater than 0"
+          ": value=" << cmdLine.timeout;
+        throw ex;
+      }
+      break;
+    case ':':
+      {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "The -" << (char)optopt
+          << " option requires a parameter";
+        throw ex;
+      }
+      break;
+    case '?':
+      {
+        castor::exception::InvalidArgument ex;
+
+        if(optopt == 0) {
+          ex.getMessage() << "Unknown command-line option";
+        } else {
+          ex.getMessage() << "Unknown command-line option: -" << (char)optopt;
+        }
+        throw ex;
+      }
+      break;
+    default:
+      {
+        castor::exception::Internal ex;
+        ex.getMessage() <<
+          "getopt_long returned the following unknown value: 0x" <<
+          std::hex << (int)c;
+        throw ex;
+      }
+    } // switch (c)
+  } // while ((c = getopt_long( ... )) != -1)
+
+  // There is no need to continue parsing when the help option is set
+  if(cmdLine.help) {
+    return cmdLine;
+  }
+
+  // Calculate the number of non-option ARGV-elements
+  const int nbArgs = argc - optind;
+
+  // Check that both VID and DRIVE has been specified
+  if(nbArgs < 2) {
+    castor::exception::MissingOperand ex;
+    ex.getMessage() << "Both VID and DRIVE must be specified";
+    throw ex;
+  }
+
+  // Parse the VID command-line argument
+  cmdLine.volId = m_acs.str2Volid(argv[optind]);
+
+  // Move on to the next command-line argument
+  optind++;
+
+  // Parse the DRIVE command-line argument
+  cmdLine.driveId = m_acs.str2DriveId(argv[optind]);
+
+  return cmdLine;
+}
+
+//------------------------------------------------------------------------------
+// usage
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsMountCmd::usage(std::ostream &os)
+  const throw() {
+  os <<
+  "Usage:\n"
+  "  mountacs [options] VID DRIVE\n"
+  "\n"
+  "Where:\n"
+  "\n"
+  "  VID    The VID of the volume to be mounted.\n"
+  "  DRIVE  The ID of the drive into which the volume is to be mounted.\n"
+  "         Drive ID format is ACS:LSM:panel:transport\n"
+  "\n"
+  "Options:\n"
+  "\n"
+  "  -d|--debug            Turn on the printing of debug information.\n"
+  "  -h|--help             Print this help message and exit.\n"
+  "  -q|--query SECONDS    Time to wait between queries to ACS for responses.\n"
+  "                        SECONDS must be an integer value greater than 0.\n"
+  "                        The default value of SECONDS is "
+    << m_defaultQueryInterval << ".\n"
+  "  -r|--readOnly         Request the volume is mounted for read-only access\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 "
+    << m_defaultTimeout << ".\n"
+  "\n"
+  "Comments to: Castor.Support@cern.ch" << std::endl;
+}
+
+//------------------------------------------------------------------------------
+// syncMount
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsMountCmd::syncMount()
+  throw(castor::exception::MountFailed) {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendMountRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, m_cmdLine.queryInterval,
+      m_cmdLine.timeout);
+    processMountResponse(buf);
+  } catch(castor::exception::Exception &ex) {
+    castor::exception::MountFailed mf;
+    mf.getMessage() << "Failed to mount volume " <<
+      m_cmdLine.volId.external_label << ": " << ex.getMessage().str();
+    throw mf;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendMountRequest
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsMountCmd::sendMountRequest(
+  const SEQ_NO seqNumber) throw(castor::exception::MountFailed) {
+  const LOCKID lockId = 0; // No lock
+  const BOOLEAN bypass = FALSE;
+
+  m_dbg << "Calling Acs::mount()" << std::endl;
+  const STATUS s = m_acs.mount(seqNumber, lockId, m_cmdLine.volId,
+    m_cmdLine.driveId, m_cmdLine.readOnly, bypass);
+  m_dbg << "Acs::mount() returned " << acs_status(s) << std::endl;
+
+  if(STATUS_SUCCESS != s) {
+    castor::exception::MountFailed ex;
+    ex.getMessage() << "Failed to send request to mount volume " <<
+      m_cmdLine.volId.external_label << " into drive " <<
+      m_acs.driveId2Str(m_cmdLine.driveId) << ": readOnly=" <<
+      (m_cmdLine.readOnly ? "TRUE" : "FALSE") << ": " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// processMountResponse
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsMountCmd::processMountResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+  throw(castor::exception::MountFailed) {
+  const ACS_MOUNT_RESPONSE *const msg = (ACS_MOUNT_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->mount_status) {
+    castor::exception::MountFailed ex;
+    ex.getMessage() << "Status of mount response is not success: " <<
+      acs_status(msg->mount_status);
+    throw(ex);
+  }
+}
diff --git a/castor/tape/rmc/AcsMountCmd.hpp b/castor/tape/rmc/AcsMountCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ac0f7e22637f6ad08a10bcdee82e00b2090d336
--- /dev/null
+++ b/castor/tape/rmc/AcsMountCmd.hpp
@@ -0,0 +1,144 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsMountCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSMOUNTCMD_HPP
+#define CASTOR_TAPE_RMC_ACSMOUNTCMD_HPP 1
+
+#include "castor/exception/Internal.hpp"
+#include "castor/exception/InvalidArgument.hpp"
+#include "castor/exception/MissingOperand.hpp"
+#include "castor/exception/MountFailed.hpp"
+#include "castor/tape/rmc/AcsCmd.hpp"
+#include "castor/tape/rmc/AcsMountCmdLine.hpp"
+
+#include <stdint.h>
+
+namespace castor  {
+namespace tape    {
+namespace rmc {
+
+/**
+ * The class implementing the mount command.
+ */
+class AcsMountCmd: public AcsCmd {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  AcsMountCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw();
+
+  /**
+   * Destructor.
+   */
+  virtual ~AcsMountCmd() throw();
+
+  /**
+   * The entry function of the command.
+   *
+   * @param argc The number of command-line arguments.
+   * @param argv The command-line arguments.
+   */
+  int main(const int argc, char *const *const argv) throw();
+
+protected:
+
+  /**
+   * Parses the specified command-line arguments.
+   *
+   * @param argc Argument count from the executable's entry function: main().
+   * @param argv Argument vector from the executable's entry function: main().
+   * @return The parsed command-line.
+   */
+  AcsMountCmdLine parseCmdLine(const int argc, char *const *const argv)
+    throw(castor::exception::Internal, castor::exception::InvalidArgument,
+      castor::exception::MissingOperand);
+
+  /**
+   * Writes the command-line usage message of to the specified output stream.
+   *
+   * @param os Output stream to be written to.
+   */
+  void usage(std::ostream &os) const throw();
+
+  /**
+   * Mounts the tape with the specified VID into the drive with the specified
+   * drive ID.
+   *
+   * This method does not return until the mount has either suceeded, failed or
+   * the specified timeout has been reached.
+   */
+  void syncMount() throw(castor::exception::MountFailed);
+
+  /**
+   * Sends the mount request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendMountRequest(const SEQ_NO seqNumber)
+    throw(castor::exception::MountFailed);
+
+  /**
+   * Throws castor::exception::DismountFailed if the mount was not
+   * successful.
+   *
+   * @param buf The mount-response message.
+   */
+  void processMountResponse(
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+    throw(castor::exception::MountFailed);
+
+private:
+
+  /**
+   * The parsed command-line.
+   *
+   * The value of this member variable is set within the main() method of this
+   * class.
+   */
+  AcsMountCmdLine m_cmdLine;
+
+  /**
+   * The default time in seconds to wait between queries to ACS for responses.
+   */
+  const int m_defaultQueryInterval;
+
+  /**
+   * The default timeout value in seconds for the mount to conclude either
+   * success or failure.
+   */
+  const int m_defaultTimeout;
+}; // class AcsMountCmd
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+#endif // CASTOR_TAPE_RMC_ACSMOUNTCMD_HPP
diff --git a/castor/tape/rmc/AcsMountCmdLine.cpp b/castor/tape/rmc/AcsMountCmdLine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6e27e6dc287b15d0c055ab846459bee6f078799d
--- /dev/null
+++ b/castor/tape/rmc/AcsMountCmdLine.cpp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsMountCmdLine.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsMountCmdLine.hpp"
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+castor::tape::rmc::AcsMountCmdLine::AcsMountCmdLine() throw():
+  debug(false),
+  help(false),
+  queryInterval(0),
+  readOnly(FALSE),
+  timeout(0) {
+  driveId.panel_id.lsm_id.acs = (ACS)0;
+  driveId.panel_id.lsm_id.lsm = (LSM)0;
+  driveId.panel_id.panel = (PANEL)0;
+  driveId.drive = (DRIVE)0;
+  memset(volId.external_label, '\0', sizeof(volId.external_label));
+}
diff --git a/castor/tape/rmc/AcsMountCmdLine.hpp b/castor/tape/rmc/AcsMountCmdLine.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ab140dd1347cd024d77b358c1cebd354060006e2
--- /dev/null
+++ b/castor/tape/rmc/AcsMountCmdLine.hpp
@@ -0,0 +1,93 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsMountCmdLine.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSMOUNTCMDLINE_HPP
+#define CASTOR_TAPE_RMC_ACSMOUNTCMDLINE_HPP 1
+
+extern "C" {
+#include "acssys.h"
+#include "acsapi.h"
+}
+
+#include <string>
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * Data type used to store the results of parsing the command-line.
+ */
+struct AcsMountCmdLine {
+  /**
+   * Constructor.
+   *
+   * Initialises all BOOLEAN member-variables to FALSE, all integer
+   * member-variables to 0 and the volume identifier to an empty string.
+   */
+  AcsMountCmdLine() throw();
+
+  /**
+   * True if the debug option has been set.
+   */
+  bool debug;
+
+  /**
+   * True if the help option has been set.
+   */
+  bool help;
+
+  /**
+   * Time in seconds to wait between queries to ACS for responses.
+   */
+  int queryInterval;
+
+  /**
+   * True if the tape is to be mount for read-only access.
+   */
+  BOOLEAN readOnly;
+
+  /**
+   * Time in seconds to wait for the dismount to conclude.
+   */
+  int timeout;
+
+  /**
+   * The volume identifier of the tape to be mounted.
+   */
+  VOLID volId;
+
+  /**
+   * The identifier of the drive into which the tape is to be mounted.
+   */
+  DRIVEID driveId;
+
+}; // class AcsMountCmdLine
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_ACSMOUNTCMDLINE_HPP
diff --git a/castor/tape/rmc/AcsMountMain.cpp b/castor/tape/rmc/AcsMountMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3b7d16b887785a081e1daf09ddbed824bd565f4
--- /dev/null
+++ b/castor/tape/rmc/AcsMountMain.cpp
@@ -0,0 +1,40 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsMountMain.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+ 
+#include "castor/tape/rmc/AcsImpl.hpp"
+#include "castor/tape/rmc/AcsMountCmd.hpp"
+
+#include <iostream>
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int main(const int argc, char *const *const argv) {
+
+  castor::tape::rmc::AcsImpl acs;
+  castor::tape::rmc::AcsMountCmd
+    cmd(std::cin, std::cout, std::cerr, acs);
+
+  return cmd.main(argc, argv);
+}
diff --git a/castor/tape/rmc/AcsQueryVolumeCmd.cpp b/castor/tape/rmc/AcsQueryVolumeCmd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..019532e939b904291493fc70e94cbb7b56508ee4
--- /dev/null
+++ b/castor/tape/rmc/AcsQueryVolumeCmd.cpp
@@ -0,0 +1,352 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsQueryVolumeCmd.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsQueryVolumeCmd.hpp"
+#include "castor/tape/utils/utils.hpp"
+
+#include <getopt.h>
+#include <iostream>
+#include <string.h>
+ 
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsQueryVolumeCmd::AcsQueryVolumeCmd(
+  std::istream &inStream, std::ostream &outStream, std::ostream &errStream,
+  Acs &acs) throw():
+  AcsCmd(inStream, outStream, errStream, acs), m_defaultQueryInterval(1),
+  m_defaultTimeout(20) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsQueryVolumeCmd::~AcsQueryVolumeCmd() throw() {
+  // Do nothing
+}
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int castor::tape::rmc::AcsQueryVolumeCmd::main(const int argc,
+  char *const *const argv) throw() {
+  try {
+    m_cmdLine = parseCmdLine(argc, argv);
+  } catch(castor::exception::InvalidArgument &ia) {
+    m_err << "Aborting: Invalid command-line: " << ia.getMessage().str() <<
+      std::endl;
+    m_err << std::endl;
+    usage(m_err);
+    return 1;
+  } catch(castor::exception::MissingOperand &mo) {
+    m_err << "Aborting: Missing operand: " << mo.getMessage().str() <<
+      std::endl;
+    m_err << std::endl;
+    usage(m_err);
+    return 1;
+  } catch(castor::exception::Internal &ie) {
+    m_err << "Aborting: Internal error: " << ie.getMessage().str() <<
+      std::endl;
+    return 1;
+  }
+
+  // Display the usage message to standard out and exit with success if the
+  // user requested help
+  if(m_cmdLine.help) {
+    usage(m_out);
+    return 0;
+  }
+
+  // Setup debug mode to be on or off depending on the command-line arguments
+  m_debugBuf.setDebug(m_cmdLine.debug);
+
+  m_dbg << "query = " << m_cmdLine.queryInterval << std::endl;
+  m_dbg << "timeout = " << m_cmdLine.timeout << std::endl;
+  m_dbg << "VID = " << m_cmdLine.volId.external_label << std::endl;
+
+  try {
+    syncQueryVolume();
+  } catch(castor::exception::QueryVolumeFailed &ex) {
+    m_err << "Aborting: " << ex.getMessage().str() << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+//------------------------------------------------------------------------------
+// parseCmdLine
+//------------------------------------------------------------------------------
+castor::tape::rmc::AcsQueryVolumeCmdLine
+  castor::tape::rmc::AcsQueryVolumeCmd::parseCmdLine(
+  const int argc, char *const *const argv)
+  throw(castor::exception::Internal, castor::exception::InvalidArgument,
+    castor::exception::MissingOperand) {
+
+  static struct option longopts[] = {
+    {"debug", 0, NULL, 'd'},
+    {"help" , 0, NULL, 'h'},
+    {"query" , required_argument, NULL, 'q'},
+    {"timeout" , required_argument, NULL, 't'},
+    {NULL, 0, NULL, 0}
+  };
+  AcsQueryVolumeCmdLine cmdLine;
+  char c;
+
+  // Set the query option to the default value
+  cmdLine.queryInterval = m_defaultQueryInterval;
+
+  // Set timeout option to the default value
+  cmdLine.timeout = m_defaultTimeout;
+
+  // Prevent getopt() from printing an error message if it does not recognize
+  // an option character
+  opterr = 0;
+  while((c = getopt_long(argc, argv, ":dhq:t:", longopts, NULL)) != -1) {
+
+    switch (c) {
+    case 'd':
+      cmdLine.debug = true;
+      break;
+    case 'h':
+      cmdLine.help = true;
+      break;
+    case 'q':
+      cmdLine.queryInterval = atoi(optarg);
+      if(0 >= cmdLine.queryInterval) {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "Query value must be an integer greater than 0"
+          ": value=" << cmdLine.queryInterval;
+        throw ex;
+      }
+      break;
+    case 't':
+      cmdLine.timeout = atoi(optarg);
+      if(0 >= cmdLine.timeout) {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "Timeout value must be an integer greater than 0"
+          ": value=" << cmdLine.timeout;
+        throw ex;
+      }
+      break;
+    case ':':
+      {
+        castor::exception::InvalidArgument ex;
+        ex.getMessage() << "The -" << (char)optopt
+          << " option requires a parameter";
+        throw ex;
+      }
+      break;
+    case '?':
+      {
+        castor::exception::InvalidArgument ex;
+
+        if(optopt == 0) {
+          ex.getMessage() << "Unknown command-line option";
+        } else {
+          ex.getMessage() << "Unknown command-line option: -" << (char)optopt;
+        }
+        throw ex;
+      }
+      break;
+    default:
+      {
+        castor::exception::Internal ex;
+        ex.getMessage() <<
+          "getopt_long returned the following unknown value: 0x" <<
+          std::hex << (int)c;
+        throw ex;
+      }
+    } // switch (c)
+  } // while ((c = getopt_long( ... )) != -1)
+
+  // There is no need to continue parsing when the help option is set
+  if(cmdLine.help) {
+    return cmdLine;
+  }
+
+  // Calculate the number of non-option ARGV-elements
+  const int nbArgs = argc - optind;
+
+  // Check that VID has been specified
+  if(nbArgs < 1) {
+    castor::exception::MissingOperand ex;
+    ex.getMessage() << "VID must be specified";
+    throw ex;
+  }
+
+  // Parse the VID command-line argument
+  cmdLine.volId = m_acs.str2Volid(argv[optind]);
+
+  return cmdLine;
+}
+
+//------------------------------------------------------------------------------
+// usage
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsQueryVolumeCmd::usage(std::ostream &os)
+  const throw() {
+  os <<
+  "Usage:\n"
+  "  queryvolumeacs [options] VID\n"
+  "\n"
+  "Where:\n"
+  "\n"
+  "  VID    The VID of the volume to be queried.\n"
+  "\n"
+  "Options:\n"
+  "\n"
+  "  -d|--debug            Turn on the printing of debug information.\n"
+  "  -h|--help             Print this help message and exit.\n"
+  "  -q|--query SECONDS    Time to wait between queries to ACS for responses.\n"
+  "                        SECONDS must be an integer value greater than 0.\n"
+  "                        The default value of SECONDS is "
+    << m_defaultQueryInterval << ".\n"
+  "  -t|--timeout SECONDS  Time to wait for the query to conclude. SECONDS\n"
+  "                        must be an integer value greater than 0.  The\n"
+  "                        default value of SECONDS in "
+    << m_defaultTimeout << ".\n"
+  "\n"
+  "Comments to: Castor.Support@cern.ch" << std::endl;
+}
+
+//------------------------------------------------------------------------------
+// syncQueryVolume
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsQueryVolumeCmd::syncQueryVolume()
+  throw(castor::exception::QueryVolumeFailed) {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendQueryVolumeRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, m_cmdLine.queryInterval,
+      m_cmdLine.timeout);
+    processQueryResponse(m_out, buf);
+  } catch(castor::exception::Exception &ex) {
+    castor::exception::QueryVolumeFailed qf;
+    qf.getMessage() << "Failed to query volume " <<
+      m_cmdLine.volId.external_label << ": " << ex.getMessage().str();
+    throw qf;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendQueryVolumeRequest
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsQueryVolumeCmd::sendQueryVolumeRequest(
+  const SEQ_NO seqNumber) throw (castor::exception::QueryVolumeFailed) {
+  VOLID volIds[MAX_ID];
+
+  memset(volIds, '\0', sizeof(volIds));
+  strncpy(volIds[0].external_label, m_cmdLine.volId.external_label,
+    sizeof(volIds[0].external_label));
+  volIds[0].external_label[sizeof(volIds[0].external_label) - 1]  = '\0';
+
+  m_dbg << "Calling Acs::queryVolume()" << std::endl;
+  const STATUS s = m_acs.queryVolume(seqNumber, volIds, 1);
+  m_dbg << "Acs::queryVolume() returned " << acs_status(s) << std::endl;
+
+  if(STATUS_SUCCESS != s) {
+    castor::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Failed to send query request for volume " <<
+      m_cmdLine.volId.external_label << ": " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// processQueryResponse
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsQueryVolumeCmd::processQueryResponse(
+  std::ostream &os,
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+  throw(castor::exception::QueryVolumeFailed) {
+
+  const ACS_QUERY_VOL_RESPONSE *const msg = (ACS_QUERY_VOL_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->query_vol_status) {
+    castor::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Status of query response is not success: " <<
+      acs_status(msg->query_vol_status);
+    throw(ex);
+  }
+
+  if((unsigned short)1 != msg->count) {
+    castor::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Query response does not contain a single volume: count="
+      << msg->count;
+    throw ex;
+  }
+
+  // count is 1 so it is safe to make a reference to the single volume status
+  const QU_VOL_STATUS &volStatus = msg->vol_status[0];
+
+  if(strcmp(m_cmdLine.volId.external_label, volStatus.vol_id.external_label)) {
+    castor::exception::QueryVolumeFailed ex;
+    ex.getMessage() <<
+      "Volume identifier of query response does not match that of request"
+      ": requestVID=" << m_cmdLine.volId.external_label <<
+      " responseVID=" << volStatus.vol_id.external_label;
+    throw ex;
+  }
+
+  writeVolumeStatus(os, volStatus);
+}
+
+//------------------------------------------------------------------------------
+// writeVolumeStatus
+//------------------------------------------------------------------------------
+void castor::tape::rmc::AcsQueryVolumeCmd::writeVolumeStatus(
+  std::ostream &os, const QU_VOL_STATUS &s) throw() {
+  os << "Volume identifier: " << s.vol_id.external_label << std::endl;
+  os << "Media type (media_types.dat): " << (int)s.media_type << std::endl;
+
+  switch(s.location_type) {
+  case LOCATION_CELL: {
+    os << "Location type: cell" << std::endl;
+    const CELLID &cellId = s.location.cell_id;
+    os << "ACS: " << (int)cellId.panel_id.lsm_id.acs << std::endl;
+    os << "LSM: " << (int)cellId.panel_id.lsm_id.lsm << std::endl;
+    os << "Panel: " << (int)cellId.panel_id.panel << std::endl;
+    os << "Row: " << (int)cellId.row << std::endl;
+    os << "Column: " << (int)cellId.col << std::endl;
+    break;
+  }
+  case LOCATION_DRIVE: {
+    os << "Location type: drive" << std::endl;
+    const DRIVEID &driveId = s.location.drive_id;
+    os << "ACS: " << (int)driveId.panel_id.lsm_id.acs << std::endl;
+    os << "LSM: " << (int)driveId.panel_id.lsm_id.lsm << std::endl;
+    os << "Panel: " << (int)driveId.panel_id.panel << std::endl;
+    os << "Drive: " << (int)driveId.drive << std::endl;
+    break;
+  }
+  default:
+    os << "Location type: UNKNOWN" << std::endl;
+    break;
+  }
+
+  os << "Status: " << acs_status(s.status) << std::endl;
+}
diff --git a/castor/tape/rmc/AcsQueryVolumeCmd.hpp b/castor/tape/rmc/AcsQueryVolumeCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..139650c015a912a18f7dcc18c9bba0ab8eb5f2ac
--- /dev/null
+++ b/castor/tape/rmc/AcsQueryVolumeCmd.hpp
@@ -0,0 +1,159 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsQueryVolumeCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSQUERYVOLUMECMD_HPP
+#define CASTOR_TAPE_RMC_ACSQUERYVOLUMECMD_HPP 1
+
+#include "castor/exception/Internal.hpp"
+#include "castor/exception/InvalidArgument.hpp"
+#include "castor/exception/MissingOperand.hpp"
+#include "castor/exception/QueryVolumeFailed.hpp"
+#include "castor/tape/rmc/AcsCmd.hpp"
+#include "castor/tape/rmc/AcsQueryVolumeCmdLine.hpp"
+
+#include <stdint.h>
+
+namespace castor  {
+namespace tape    {
+namespace rmc {
+
+/**
+ * The class implementing the mount command.
+ */
+class AcsQueryVolumeCmd: public AcsCmd {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  AcsQueryVolumeCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw();
+
+  /**
+   * Destructor.
+   */
+  virtual ~AcsQueryVolumeCmd() throw();
+
+  /**
+   * The entry function of the command.
+   *
+   * @param argc The number of command-line arguments.
+   * @param argv The command-line arguments.
+   */
+  int main(const int argc, char *const *const argv) throw();
+
+protected:
+
+  /**
+   * Parses the specified command-line arguments.
+   *
+   * @param argc Argument count from the executable's entry function: main().
+   * @param argv Argument vector from the executable's entry function: main().
+   * @return The parsed command-line.
+   */
+  AcsQueryVolumeCmdLine parseCmdLine(const int argc, char *const *const argv)
+    throw(castor::exception::Internal, castor::exception::InvalidArgument,
+      castor::exception::MissingOperand);
+
+  /**
+   * Writes the command-line usage message of to the specified output stream.
+   *
+   * @param os Output stream to be written to.
+   */
+  void usage(std::ostream &os) const throw();
+
+  /**
+   * Queries ACS for information about the volume identifier specified on the
+   * command-line.
+   *
+   * This method does not return until the information has been successfully
+   * retrieved, an error has occurred or the specified timeout has been
+   * reached.
+   *
+   * @return The volume status of the volume identifier specified on the
+   * command-line.
+   */
+  void syncQueryVolume() throw(castor::exception::QueryVolumeFailed);
+
+  /**
+   * Sends the query volume  request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendQueryVolumeRequest(const SEQ_NO seqNumber)
+    throw (castor::exception::QueryVolumeFailed);
+
+  /**
+   * Extracts the volume status from the specified query-response message and
+   * writes it in human-readable form to the specified output stream.
+   *
+   * @param os The output stream.
+   * @param buf The query-response message.
+   */
+  void processQueryResponse(std::ostream &os,
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
+    throw(castor::exception::QueryVolumeFailed);
+
+  /**
+   * Writes a human readable representation of the specified volume status to
+   * the specified output stream.
+   *
+   * @param os The output stream.
+   * @param s The volume status.
+   */
+  void writeVolumeStatus(std::ostream &os, const QU_VOL_STATUS &s) throw();
+
+private:
+
+  /**
+   * The parsed command-line.
+   *
+   * The value of this member variable is set within the main() method of this
+   * class.
+   */
+  AcsQueryVolumeCmdLine m_cmdLine;
+
+  /**
+   * The default time in seconds to wait between queries to ACS for responses.
+   */
+  const int m_defaultQueryInterval;
+
+  /**
+   * The default timeout value in seconds for the query to conclude either
+   * success or failure.
+   */
+  const int m_defaultTimeout;
+
+}; // class AcsQueryVolumeCmd
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+#endif // CASTOR_TAPE_RMC_ACSQUERYVOLUMECMD_HPP
diff --git a/castor/tape/rmc/AcsQueryVolumeCmdLine.cpp b/castor/tape/rmc/AcsQueryVolumeCmdLine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..870adc3d1fdc5b78ad4c0e0c4d01e8a03c53a79d
--- /dev/null
+++ b/castor/tape/rmc/AcsQueryVolumeCmdLine.cpp
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsQueryVolumeCmdLine.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/AcsQueryVolumeCmdLine.hpp"
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+castor::tape::rmc::AcsQueryVolumeCmdLine::AcsQueryVolumeCmdLine()
+  throw():
+  debug(FALSE),
+  help(FALSE),
+  queryInterval(0),
+  timeout(0) {
+  memset(volId.external_label, '\0', sizeof(volId.external_label));
+}
diff --git a/castor/tape/rmc/AcsQueryVolumeCmdLine.hpp b/castor/tape/rmc/AcsQueryVolumeCmdLine.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b9bda6e2a570819867d7a73320b5e7984b65157a
--- /dev/null
+++ b/castor/tape/rmc/AcsQueryVolumeCmdLine.hpp
@@ -0,0 +1,83 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsQueryVolumeCmdLine.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_ACSQUERYVOLUMECMDLINE_HPP
+#define CASTOR_TAPE_RMC_ACSQUERYVOLUMECMDLINE_HPP 1
+
+extern "C" {
+#include "acssys.h"
+#include "acsapi.h"
+}
+
+#include <string>
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * Data type used to store the results of parsing the command-line.
+ */
+struct AcsQueryVolumeCmdLine {
+  /**
+   * Constructor.
+   *
+   * Initialises all BOOLEAN member-variables to FALSE, all integer
+   * member-variables to 0 and the volume identifier to an empty string.
+   */
+  AcsQueryVolumeCmdLine() throw();
+
+  /**
+   * True if the debug option has been set.
+   */
+  BOOLEAN debug;
+
+  /**
+   * True if the help option has been set.
+   */
+  BOOLEAN help;
+
+  /**
+   * Time in seconds to wait between queries to ACS for responses.
+   */
+  int queryInterval;
+
+  /**
+   * Time in seconds to wait for the dismount to conclude.
+   */
+  int timeout;
+
+  /**
+   * The volume identifier of the tape to be mounted.
+   */
+  VOLID volId;
+
+}; // class AcsQueryVolumeCmdLine
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_ACSQUERYVOLUMECMDLINE_HPP
diff --git a/castor/tape/rmc/AcsQueryVolumeMain.cpp b/castor/tape/rmc/AcsQueryVolumeMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6cb852a1da5d0d547584408f31ec25af70d6544f
--- /dev/null
+++ b/castor/tape/rmc/AcsQueryVolumeMain.cpp
@@ -0,0 +1,40 @@
+/******************************************************************************
+ *                 castor/tape/rmc/AcsQueryVolumeMain.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+ 
+#include "castor/tape/rmc/AcsImpl.hpp"
+#include "castor/tape/rmc/AcsQueryVolumeCmd.hpp"
+
+#include <iostream>
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int main(const int argc, char *const *const argv) {
+
+  castor::tape::rmc::AcsImpl acs;
+  castor::tape::rmc::AcsQueryVolumeCmd
+    cmd(std::cin, std::cout, std::cerr, acs);
+
+  return cmd.main(argc, argv);
+}
diff --git a/castor/tape/rmc/DebugBuf.cpp b/castor/tape/rmc/DebugBuf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fdcb37f13dd76fa985249d35eeb179bdc5342907
--- /dev/null
+++ b/castor/tape/rmc/DebugBuf.cpp
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *                 castor/tape/rmc/DebugBuf.cpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/rmc/DebugBuf.hpp"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::DebugBuf::DebugBuf(std::ostream &os):
+  m_debug(false), m_os(os), m_writePreamble(true) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::rmc::DebugBuf::~DebugBuf() {
+}
+
+//------------------------------------------------------------------------------
+// setDebug
+//------------------------------------------------------------------------------
+void castor::tape::rmc::DebugBuf::setDebug(const bool value) throw() {
+  m_debug = value;
+}
+
+//------------------------------------------------------------------------------
+// overflow
+//------------------------------------------------------------------------------
+std::streambuf::int_type castor::tape::rmc::DebugBuf::overflow(
+  const int_type c) {
+  // Only write something if debug mode is on
+  if(m_debug) {
+    if(m_writePreamble) {
+      writePreamble();
+      m_writePreamble = false;
+    }
+    m_os << (char)c;
+  }
+
+  // If an end of line was encountered then the next write should be preceeded
+  // with a preamble
+  if('\n' == (char)c) {
+    m_writePreamble = true;
+  }
+
+  return c;
+}
+
+//------------------------------------------------------------------------------
+// writePreamble
+//------------------------------------------------------------------------------
+void castor::tape::rmc::DebugBuf::writePreamble() throw() {
+  m_os << "DEBUG: ";
+}
diff --git a/castor/tape/rmc/DebugBuf.hpp b/castor/tape/rmc/DebugBuf.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..11a98cb2cf6b313be4157c7589c8449698afc594
--- /dev/null
+++ b/castor/tape/rmc/DebugBuf.hpp
@@ -0,0 +1,105 @@
+/******************************************************************************
+ *                 castor/tape/rmc/DebugBuf.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef CASTOR_TAPE_RMC_DEBUGBUF_HPP
+#define CASTOR_TAPE_RMC_DEBUGBUF_HPP 1
+
+#include <ostream>
+#include <streambuf>
+
+namespace castor {
+namespace tape {
+namespace rmc {
+
+/**
+ * Stream buffer class used to prepend a standard preamble to debug
+ * message-lines.
+ *
+ * This stream buffer does not write any output if debug mode has not been
+ * turned on by calling setDebugMode(true).  Any debug message written to this
+ * stream buffer will be discarded if debug mode is off.
+ */
+class DebugBuf : public std::streambuf {
+public:
+
+  /**
+   * Constructor.
+   *
+   * Initialises the the debug mode to be off.
+   *
+   * @param os The output stream to which each debug message-line togther with
+   * its standard preamble shall be written.
+   */
+  DebugBuf(std::ostream &os);
+
+  /**
+   * Destructor.
+   */
+  ~DebugBuf();
+
+  /**
+   * Set the debug mode to be on (true) or off (false).
+   *
+   * The default set in the constructor is off (false).
+   */
+  void setDebug(const bool value) throw();
+
+protected:
+
+  /**
+   * Sends the specified character to the output channnel.
+   */
+  int_type overflow (const int_type c);
+
+  /**
+   * Writes the standard preamble to the output stream.
+   */
+  void writePreamble() throw();
+
+private:
+
+  /**
+   * True if debug mode is on.
+   */
+  bool m_debug;
+
+  /**
+   * The output stream to which each debug message-line togther with its
+   * standard preamble shall be written.
+   */
+  std::ostream &m_os;
+
+  /**
+   * True is a preamble should be written.
+   */
+  bool m_writePreamble;
+
+}; // class DebugBuf
+
+} // namespace rmc
+} // namespace tape
+} // namespace castor
+
+
+#endif // CASTOR_TAPE_RMC_DEBUGBUF_HPP
diff --git a/castor/tape/rmc/Imakefile b/castor/tape/rmc/Imakefile
new file mode 100644
index 0000000000000000000000000000000000000000..cd015165cbcf45115c0849db0dbf4935b2f82ac9
--- /dev/null
+++ b/castor/tape/rmc/Imakefile
@@ -0,0 +1,100 @@
+COMM
+COMM                      castor/tape/mediachanger/Imakefile
+COMM
+COMM This file is part of the Castor project.
+COMM See http://castor.web.cern.ch/castor
+COMM
+COMM Copyright (C) 2003  CERN
+COMM This program is free software; you can redistribute it and/or
+COMM modify it under the terms of the GNU General Public License
+COMM as published by the Free Software Foundation; either version 2
+COMM of the License, or (at your option) any later version.
+COMM This program is distributed in the hope that it will be useful,
+COMM but WITHOUT ANY WARRANTY; without even the implied warranty of
+COMM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+COMM GNU General Public License for more details.
+COMM You should have received a copy of the GNU General Public License
+COMM along with this program; if not, write to the Free Software
+COMM Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+COMM
+COMM
+COMM @author Steven.Murray@cern.ch
+COMM
+
+COMM For licencing reasons the castor-tape-acs-dismount, castor-tape-acs-mount
+COMM and castor-tape-acs-queryvolume commands are not statically linked with
+COMM the CDK software provided by Oracle.  However if static linking would be
+COMM used then the following comments show how it could be done.
+COMM
+COMM libutl.a and libapi.a are circularly dependent on each other.  The
+COMM -Wl,--start-group -Wl,--end-group options of gcc are used to encompass a
+COMM list of objects that should be passed several times in order to resolve
+COMM their interdependencies.
+COMM CDKLIB = \
+COMM  -Wl,--start-group \
+COMM  $(LIBDIR)/CDK/libutl.a \
+COMM  $(LIBDIR)/CDK/libapi.a \
+COMM  -Wl,--end-group \
+COMM  $(LIBDIR)/CDK/libipc.a \
+COMM  $(LIBDIR)/CDK/libcl.a
+
+CDKLIB = -L $(LIBDIR)/CDK -lapi -lutl -lipc -lcl
+
+DependsOnLibrary(castor/tape/utils,castortapeutils)
+DependsOnLibrary(common,castorcommon)
+
+ACS_SRCS = \
+  Acs.cpp \
+  AcsImpl.cpp
+
+COMPILECPP(Acs.cpp, -DLINUX -I/usr/include/CDK)
+COMPILECPP(AcsImpl.cpp, -DLINUX -I/usr/include/CDK)
+
+ACSCOMMAND_SRCS = \
+  AcsCmd.cpp \
+  DebugBuf.cpp
+
+COMPILECPP(AcsCmd.cpp, -DLINUX -I/usr/include/CDK)
+
+ACSMOUNTBIN_SRCS = \
+  $(ACS_SRCS) \
+  $(ACSCOMMAND_SRCS) \
+  AcsMountCmd.cpp \
+  AcsMountCmdLine.cpp \
+  AcsMountMain.cpp
+ACSMOUNTBIN_OBJS = $(ACSMOUNTBIN_SRCS:.cpp=.o)
+
+COMPILECPP(AcsMountCmd.cpp, -DLINUX -I/usr/include/CDK)
+COMPILECPP(AcsMountCmdLine.cpp, -DLINUX -I/usr/include/CDK)
+COMPILECPP(AcsMountMain.cpp, -DLINUX -I/usr/include/CDK)
+
+NormalProgramTarget(castor-tape-acs-mount,$(ACSMOUNTBIN_OBJS),,$(CDKLIB),755)
+EXEMANPAGE(castor-tape-acs-mount)
+
+ACSDISMOUNTBIN_SRCS = \
+  $(ACS_SRCS) \
+  $(ACSCOMMAND_SRCS) \
+  AcsDismountCmd.cpp \
+  AcsDismountCmdLine.cpp \
+  AcsDismountMain.cpp
+ACSDISMOUNTBIN_OBJS = $(ACSDISMOUNTBIN_SRCS:.cpp=.o)
+COMPILECPP(AcsDismountCmd.cpp, -DLINUX -I/usr/include/CDK)
+COMPILECPP(AcsDismountCmdLine.cpp, -DLINUX -I/usr/include/CDK)
+COMPILECPP(AcsDismountMain.cpp, -DLINUX -I/usr/include/CDK)
+
+NormalProgramTarget(castor-tape-acs-dismount,$(ACSDISMOUNTBIN_OBJS),,$(CDKLIB),755)
+EXEMANPAGE(castor-tape-acs-dismount)
+
+ACSQUERYVOLUMEBIN_SRCS = \
+  $(ACS_SRCS) \
+  $(ACSCOMMAND_SRCS) \
+  AcsQueryVolumeCmd.cpp \
+  AcsQueryVolumeCmdLine.cpp \
+  AcsQueryVolumeMain.cpp
+ACSQUERYVOLUMEBIN_OBJS = $(ACSQUERYVOLUMEBIN_SRCS:.cpp=.o)
+COMPILECPP(AcsQueryVolumeCmd.cpp, -DLINUX -I/usr/include/CDK)
+COMPILECPP(AcsQueryVolumeCmdLine.cpp, -DLINUX -I/usr/include/CDK)
+COMPILECPP(AcsQueryVolumeMain.cpp, -DLINUX -I/usr/include/CDK)
+
+NormalProgramTarget(castor-tape-acs-queryvolume,$(ACSQUERYVOLUMEBIN_OBJS),,$(CDKLIB),755)
+EXEMANPAGE(castor-tape-acs-queryvolume)
diff --git a/castor/tape/rmc/castor-tape-acs-dismount.man b/castor/tape/rmc/castor-tape-acs-dismount.man
new file mode 100644
index 0000000000000000000000000000000000000000..20b9561e72d3d0c80b63e3e327ebc7dd2baef82e
--- /dev/null
+++ b/castor/tape/rmc/castor-tape-acs-dismount.man
@@ -0,0 +1,54 @@
+.\" 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.
+.TH CASTOR-TAPE-ACS-DISMOUNT 1 "$Date: 2009/08/07 15:56:38 $" CASTOR "CASTOR"
+.SH NAME
+castor-tape-acs-dismount \- dismount a volume
+.SH SYNOPSIS
+.BI "castor-tape-acs-dismount [options] VID DRIVE"
+
+.SH DESCRIPTION
+.B dismountacs
+dismounts the volume with the specfied VID from the drive with the specified
+DRIVE ID. The DRIVE ID format is ACS:LSM:panel:transport.
+
+.SH OPTIONS
+.TP
+\fB\-d, \-\-debug
+Turns on the printing of debug information.
+.TP
+\fB\-f, \-\-force
+Force the dismount.
+.TP
+\fB\-h, \-\-help
+Prints the usage message.
+.TP
+\fB\-q, \-\-query SECONDS
+Time wait between queries to ACLS for responses.
+SECONDS must be an integer value greater than 0.
+The default value of SECONDS is 10.
+.TP
+\fB\-t, \-\-timeout SECONDS
+Time to wait for the dismount operation to conclude.
+SECONDS must be an integer value greater than 0.
+The default value of SECONDS is 600.
+
+.SH "RETURN CODES"
+.TP
+\fB 0
+Ok.
+.TP
+\fB 1
+Command failed.
+
+.SH AUTHOR
+\fBCASTOR\fP Team <castor.support@cern.ch>
diff --git a/castor/tape/rmc/castor-tape-acs-mount.man b/castor/tape/rmc/castor-tape-acs-mount.man
new file mode 100644
index 0000000000000000000000000000000000000000..fd4c0de7b66cfb18b16772488c00b71fea69cf7f
--- /dev/null
+++ b/castor/tape/rmc/castor-tape-acs-mount.man
@@ -0,0 +1,55 @@
+.\" 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.
+.TH CASTOR-TAPE-ACS-MOUNT 1 "$Date: 2009/08/07 15:56:38 $" CASTOR "CASTOR"
+.SH NAME
+castor-tape-acs-mount \- mount a volume
+.SH SYNOPSIS
+.BI "castor-tape-acs-mount [options] VID DRIVE"
+
+.SH DESCRIPTION
+.B castor-tape-acs-mount
+mounts the volume with the specfied VID in an ACS compatible tape-library into
+the drive with the specified DRIVE ID. The DRIVE ID format is
+ACS:LSM:panel:transport.
+
+.SH OPTIONS
+.TP
+\fB\-d, \-\-debug
+Turns on the printing of debug information.
+.TP
+\fB\-h, \-\-help
+Prints the usage message.
+.TP
+\fB\-q, \-\-query SECONDS
+Time wait between queries to ACLS for responses.
+SECONDS must be an integer value greater than 0.
+The default value of SECONDS is 10.
+.TP
+\fB\-r, \-\-readonly
+Request the volume is mounted for read-only access.
+.TP
+\fB\-t, \-\-timeout SECONDS
+Time to wait for the mount operation to conclude.  
+SECONDS must be an integer value greater than 0.
+The default value of SECONDS is 600.
+
+.SH "RETURN CODES"
+.TP
+\fB 0
+Ok.
+.TP
+\fB 1
+Command failed.
+
+.SH AUTHOR
+\fBCASTOR\fP Team <castor.support@cern.ch>
diff --git a/castor/tape/rmc/castor-tape-acs-queryvolume.man b/castor/tape/rmc/castor-tape-acs-queryvolume.man
new file mode 100644
index 0000000000000000000000000000000000000000..57b35654dcd6deaef62afc7c10063efe76f3d1c5
--- /dev/null
+++ b/castor/tape/rmc/castor-tape-acs-queryvolume.man
@@ -0,0 +1,50 @@
+.\" 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.
+.TH MOUNTACS 1 "$Date: 2009/08/07 15:56:38 $" CASTOR "CASTOR"
+.SH NAME
+queryvolumeacs \- queries a volume
+.SH SYNOPSIS
+.BI "queryvolumeacs [options] VID"
+
+.SH DESCRIPTION
+.B queryvolumeacs
+quieries ACS for information about the volume with the specfied VID.
+
+.SH OPTIONS
+.TP
+\fB\-d, \-\-debug
+Turns on the printing of debug information.
+.TP
+\fB\-h, \-\-help
+Prints the usage message.
+.TP
+\fB\-q, \-\-query SECONDS
+Time wait between queries to ACLS for responses.
+SECONDS must be an integer value greater than 0.
+The default values of SECONDS is 300.
+.TP
+\fB\-t, \-\-timeout SECONDS
+Time to wait for the mount operation to conclude.  
+SECONDS must be an integer value greater than 0.
+The default value of SECONDS is 10.
+
+.SH "RETURN CODES"
+.TP
+\fB 0
+Ok.
+.TP
+\fB 1
+Command failed.
+
+.SH AUTHOR
+\fBCASTOR\fP Team <castor.support@cern.ch>
diff --git a/debian/castor-devel.install.perm b/debian/castor-devel.install.perm
index f55dfc2e5c2e61408b51662625e999a23d385c73..72148c4ab73139d892b9db0c4d32f5312cc7aedb 100644
--- a/debian/castor-devel.install.perm
+++ b/debian/castor-devel.install.perm
@@ -54,9 +54,6 @@
 %attr(0644,root,root) usr/include/shift/rfio_constants.h
 %attr(0644,root,root) usr/include/shift/rfio_errno.h
 %attr(0644,root,root) usr/include/shift/rfio_lcastorfdt.h
-%attr(0644,root,root) usr/include/shift/rmc.h
-%attr(0644,root,root) usr/include/shift/rmc_api.h
-%attr(0644,root,root) usr/include/shift/rmc_constants.h
 %attr(0644,root,root) usr/include/shift/rtcp.h
 %attr(0644,root,root) usr/include/shift/rtcp_api.h
 %attr(0644,root,root) usr/include/shift/rtcp_constants.h
@@ -64,7 +61,6 @@
 %attr(0644,root,root) usr/include/shift/sacct.h
 %attr(0644,root,root) usr/include/shift/scsictl.h
 %attr(0644,root,root) usr/include/shift/serrno.h
-%attr(0644,root,root) usr/include/shift/smc.h
 %attr(0644,root,root) usr/include/shift/stage_api.h
 %attr(0644,root,root) usr/include/shift/stage_constants.h
 %attr(0644,root,root) usr/include/shift/stage_limits.h
diff --git a/h/Imakefile b/h/Imakefile
index dd419c63564bf72aba811d5f50bc5dc142c8482b..b94d030a230587f8f7354f4e62dadc273eb774ad 100644
--- a/h/Imakefile
+++ b/h/Imakefile
@@ -73,16 +73,12 @@ ClientInstallNonExecFile(rfio.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(rfio_api.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(rfio_constants.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(rfio_errno.h,$(DESTDIRCASTOR),644)
-ClientInstallNonExecFile(rmc.h,$(DESTDIRCASTOR),644)
-ClientInstallNonExecFile(rmc_api.h,$(DESTDIRCASTOR),644)
-ClientInstallNonExecFile(rmc_constants.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(rtcp.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(rtcp_api.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(rtcp_constants.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(rtcp_server.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(scsictl.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(serrno.h,$(DESTDIRCASTOR),644)
-ClientInstallNonExecFile(smc.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(stage_api.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(stage_constants.h,$(DESTDIRCASTOR),644)
 ClientInstallNonExecFile(stage_limits.h,$(DESTDIRCASTOR),644)
diff --git a/h/rmc.h b/h/rmc.h
deleted file mode 100644
index a6a7be9fd29db065310b874ff503156cfb48b3ad..0000000000000000000000000000000000000000
--- a/h/rmc.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * $Id: rmc.h,v 1.2 2007/04/19 15:18:19 sponcec3 Exp $
- */
-
-/*
- * Copyright (C) 2001 by CERN/IT/PDP/DM
- * All rights reserved
- */
-
-/*
- */
-
-#ifndef _RMC_H
-#define _RMC_H
-
-			/* SCSI media changer server constants */
-
-#include "osdep.h"
-#include "rmc_constants.h"
-#include "smc.h"
-#define	CHECKI  5	/* max interval to check for work to be done */
-#define	MAXRETRY 5
-#define	RETRYI	60
-#define	LOGBUFSZ 1024
-#define	PRTBUFSZ 180
-#define	REPBUFSZ 524288	/* must be >= max media changer server reply size */
-#define	REQBUFSZ 256	/* must be >= max media changer server request size */
-#define RMC_MAGIC	0x120D0301
-#define RMC_TIMEOUT	5	/* netread timeout while receiving a request */
-
-int rmclogit(char*, char*, ...);
-
-#define RETURN(x) \
-	{ \
-	rmclogit (func, "returns %d\n", (x)); \
-	return ((x)); \
-	}
-
-			/* 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 */
-
-			/* 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"
-
-                        /* SCSI media changer server structures */
-
-struct extended_robot_info {
-	int	smc_fd;
-	char	smc_ldr[CA_MAXRBTNAMELEN+1];
-	int	smc_support_voltag;
-	struct robot_info robot_info;
-};
-#endif
diff --git a/h/rmc_api.h b/h/rmc_api.h
index 489bf1e93bc1aad9e70973fc73fc7cdd4cdef477..462bd1e1f2a9077919d43277dfbfb6ffe894165a 100644
--- a/h/rmc_api.h
+++ b/h/rmc_api.h
@@ -12,7 +12,8 @@
 
 #ifndef _RMC_API_H
 #define _RMC_API_H
-#include "smc.h"
+
+#include "h/smc_struct.h"
 
                         /*  function prototypes */
 
diff --git a/h/rmc_constants.h b/h/rmc_constants.h
index 159d807b12308ed60bb6797490640f9e3b9473af..02887bb496fc94e67293d4d1153635543180b8c6 100644
--- a/h/rmc_constants.h
+++ b/h/rmc_constants.h
@@ -14,6 +14,15 @@
 #define _RMC_CONSTANTS_H
 #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
 
 			/* SCSI media changer utilities exit codes */
@@ -21,4 +30,37 @@
 #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"
+
 #endif
diff --git a/h/rmc_logit.h b/h/rmc_logit.h
new file mode 100644
index 0000000000000000000000000000000000000000..607abb6ce569c9fc610a0e4fe12f6b37d9a99a73
--- /dev/null
+++ b/h/rmc_logit.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2001 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+
+#ifndef _RMC_LOGIT_H
+#define _RMC_LOGIT_H 1
+
+int rmc_logit(const char *const func, const char *const msg, ...);
+
+#endif
diff --git a/h/rmc_logreq.h b/h/rmc_logreq.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa2437b153406a0fb1aec41fc3a7a0de33c0412d
--- /dev/null
+++ b/h/rmc_logreq.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2001 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+
+#ifndef _RMC_LOGREQ_H
+#define _RMC_LOGREQ_H 1
+
+void rmc_logreq(const char *const func, char *const logbuf);
+
+#endif
diff --git a/h/rmc_marshall_element.h b/h/rmc_marshall_element.h
new file mode 100644
index 0000000000000000000000000000000000000000..29c014adde578cdd76def5ae3354ec1a64fdbab6
--- /dev/null
+++ b/h/rmc_marshall_element.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2001 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+
+#ifndef _RMC_MARSHALL_ELEMENT_H
+#define _RMC_MARSHALL_ELEMENT_H 1
+
+#include "h/smc_struct.h"
+
+int rmc_marshall_element (char **const sbpp, const struct smc_element_info *const element_info);
+
+#endif
diff --git a/h/rmc_procreq.h b/h/rmc_procreq.h
new file mode 100644
index 0000000000000000000000000000000000000000..00c5e899726e3540e80413eeb8ed07a165e447a1
--- /dev/null
+++ b/h/rmc_procreq.h
@@ -0,0 +1,46 @@
+/******************************************************************************
+ *                      rmc_procreq.h
+ *
+ * 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
+ *****************************************************************************/
+
+#ifndef _RMC_PROCREQ_H
+#define _RMC_PROCREQ_H 1
+
+struct rmc_srv_rqst_context {
+  const char *localhost;
+  int rpfd;
+  char *req_data;
+  const char *clienthost;
+};
+
+int rmc_srv_export(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_findcart(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_getgeom(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_import(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_mount(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_readelem(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_unmount(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_genericmount(struct rmc_srv_rqst_context *const rqst_context);
+int rmc_srv_genericunmount(struct rmc_srv_rqst_context *const rqst_context);
+
+#endif
diff --git a/h/rmc_send_scsi_cmd.h b/h/rmc_send_scsi_cmd.h
new file mode 100644
index 0000000000000000000000000000000000000000..0112e93985aaef4a77abe8981a37a92334db7b0c
--- /dev/null
+++ b/h/rmc_send_scsi_cmd.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *                      rmc_send_scsi_cmd.h
+ *
+ * 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 Sebastien Ponce
+ *****************************************************************************/
+
+#ifndef H_RMC_SEND_SCSI_CMD_H 
+#define H_RMC_SEND_SCSI_CMD_H 1
+
+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 timeout,   /* in milliseconds */
+  const int flags,
+  int *const nb_sense_ret,
+  char **const msgaddr);
+
+#endif /* H_RMC_SEND_SCSI_CMD_H */
diff --git a/h/rmc_sendrep.h b/h/rmc_sendrep.h
new file mode 100644
index 0000000000000000000000000000000000000000..f8c5b8453dd6539701463a5d6540f4adacfcaa4e
--- /dev/null
+++ b/h/rmc_sendrep.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 1998-2002 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+
+#ifndef _RMC_SENDREP_H
+#define _RMC_SENDREP_H 1
+
+int rmc_sendrep(const int rpfd, const int rep_type, ...);
+
+#endif
diff --git a/h/rmc_server_api.h b/h/rmc_server_api.h
deleted file mode 100644
index 1366fceb2a7357eb89368e22ae3d890790e7395d..0000000000000000000000000000000000000000
--- a/h/rmc_server_api.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- */
-
-#ifndef _RMC_SERVER_API_H
-#define _RMC_SERVER_API_H
-
-                        /*  function prototypes */
-
-EXTERN_C int rmc_srv_export (char*, char*);
-EXTERN_C int rmc_srv_findcart (char*, char*);
-EXTERN_C int rmc_srv_getgeom (char*, char*);
-EXTERN_C int rmc_srv_import (char*, char*);
-EXTERN_C int rmc_srv_mount (char*, char*);
-EXTERN_C int rmc_srv_readelem (char*, char*);
-EXTERN_C int rmc_srv_unmount (char*, char*);
-
-#endif
diff --git a/h/rmc_smcsubr.h b/h/rmc_smcsubr.h
new file mode 100644
index 0000000000000000000000000000000000000000..a84ef69aaf62665be03659d5cb91258e99edc9fd
--- /dev/null
+++ b/h/rmc_smcsubr.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ *                 h/rmc_smcsubr.h
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef H_RMC_SMCSUBR_H
+#define H_RMC_SMCSUBR_H 1
+
+#include "h/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_cartridge2 (
+  const int fd,
+  const char *const rbtdev,
+  const char *const 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 template,
+  const int type,
+  const int start,
+  const int nbelem,
+  struct smc_element_info element_info[]);
+
+int smc_lasterror(
+  struct smc_status *const smc_stat,
+  char **const msgaddr);
+
+int smc_move_medium(
+  const int fd,
+  const char *const rbtdev,
+  const int from,
+  const int to,
+  const int invert);
+
+#endif /* H_RMC_SMCSUBR_H */
diff --git a/h/rmc_smcsubr2.h b/h/rmc_smcsubr2.h
new file mode 100644
index 0000000000000000000000000000000000000000..448e488ac9258975ed23653e553dcc7812cae7dc
--- /dev/null
+++ b/h/rmc_smcsubr2.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 1998-2002 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+
+#ifndef _RMC_SMCSUBR2_H
+#define _RMC_SMCSUBR2_H 1
+
+#include "h/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,
+  struct robot_info *const robot_info,
+  const int drvord,
+  const char *const vid,
+  const int invert);
+
+#endif
diff --git a/h/sendscsicmd.h b/h/sendscsicmd.h
index cdb05683d80c19ed76f753d524ce6c756859b9df..185f92c2e11d29ad8b6ddbea58da20c0593b7f49 100644
--- a/h/sendscsicmd.h
+++ b/h/sendscsicmd.h
@@ -26,7 +26,19 @@
 #ifndef H_SENDSCSISMD_H 
 #define H_SENDSCSISMD_H 1
 
-int send_scsi_cmd (int, char *, int, unsigned char *, int, unsigned char *,
-                   int, char *, int, int, int, int *, char **); 
+int 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 timeout,   /* in milliseconds */
+  const int flags,
+  int *const nb_sense_ret,
+  char **const msgaddr);
 
 #endif /* H_SENDSCSISMD_H */
diff --git a/h/smc.h b/h/smc.h
deleted file mode 100644
index 4b9cdfa766651a2f033cd4377ed66cd5ca57064c..0000000000000000000000000000000000000000
--- a/h/smc.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * $Id: smc.h,v 1.10 2007/03/26 12:15:51 wiebalck Exp $
- */
-
-/*
- * Copyright (C) 1998-2002 by CERN/IT/PDP/DM
- * All rights reserved
- */
-
-/*
- */
-
-#ifndef _SMC_H
-#define _SMC_H
-
-			/* 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"
-
-			/* smc structures */
-
-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 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 */
-};
-
-EXTERN_C int smc_get_geometry     (int, char*, struct robot_info*);
-EXTERN_C int smc_move_medium      (int, char*, int, int, int);
-EXTERN_C int smc_lasterror        (struct smc_status *, char**);
-EXTERN_C int smc_read_elem_status (int, char*, int, int, int, struct smc_element_info[]);
-EXTERN_C int smc_find_cartridge   (int, char*, char*, int, int, int, struct smc_element_info[]);
-EXTERN_C int smc_find_cartridge2  (int, char*, char*, int, int, int, struct smc_element_info[]);
-
-EXTERN_C int smc_dismount         (int, char*, struct robot_info*, int, char*);
-EXTERN_C int smc_export           (int, char*, struct robot_info*, char*);
-EXTERN_C int smc_import           (int, char*, struct robot_info*, char*);
-EXTERN_C int smc_mount            (int, char*, struct robot_info*, int, char*, int);
-
-#endif
diff --git a/h/smc_constants.h b/h/smc_constants.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5eb67bef9161b69091dc568c878a8031056589c
--- /dev/null
+++ b/h/smc_constants.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 1998-2002 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+
+#ifndef _SMC_CONSTANT_H
+#define _SMC_CONSTANT_H 1
+
+			/* 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"
+
+#endif
diff --git a/h/smc_struct.h b/h/smc_struct.h
new file mode 100644
index 0000000000000000000000000000000000000000..067e7e280a41b30091dd88468e796129f360c29f
--- /dev/null
+++ b/h/smc_struct.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 1998-2002 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+
+#ifndef _SMC_STRUCT_H
+#define _SMC_STRUCT_H 1
+
+#include "h/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];
+  int     smc_support_voltag;
+  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 */
+};
+
+#endif
diff --git a/rmc/Imakefile b/rmc/Imakefile
index 26150ad14e9ec9faf33a5a838dc585c4efc26bb4..531e86271b0b8adbaa02a97c73840f1e23d01a88 100644
--- a/rmc/Imakefile
+++ b/rmc/Imakefile
@@ -11,7 +11,15 @@ include $(CASTOR_ROOT)/tape/Makefile
 
 RMCD_DEPLIBS = DepSharedLibraryTargetName(tape,castortape)
 RMCD_LIBS = $(RMCD_DEPLIBS) BuildRPathcastortape
-RMCD_OBJS = rmc_serv.o rmc_procreq.o rmclogit.o sendrep.o usrmsg.o smcsubr.o smcsubr2.o
+RMCD_OBJS = \
+  rmc_serv.o \
+  rmc_procreq.o \
+  rmc_logit.o \
+  rmc_logreq.o \
+  rmc_marshall_element.o \
+  rmc_sendrep.o \
+  rmc_smcsubr.o \
+  rmc_send_scsi_cmd.o
 TapeProgramTarget(rmcd,$(RMCD_OBJS),$(RMCD_DEPLIBS),$(RMCD_LIBS),755)
 ADMMANPAGE(rmcd)
 TapeMakeDir($(LOGPATH),0755)
diff --git a/rmc/rmc_dismount.c b/rmc/rmc_dismount.c
index 13ce8015766651aed0d591bf281383fa4656961e..6b4690177177747d55dc168dd8ca17e30ca156db 100644
--- a/rmc/rmc_dismount.c
+++ b/rmc/rmc_dismount.c
@@ -9,10 +9,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <netinet/in.h>
-#include "marshall.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/marshall.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 int rmc_dismount(
 	const char *const server,
 	const char *const vid,
@@ -25,7 +25,7 @@ int rmc_dismount(
 	char *q;
 	char repbuf[1];
 	char *sbp;
-	char sendbuf[REQBUFSZ];
+	char sendbuf[RMC_REQBUFSZ];
 	uid_t uid;
 
 	uid = getuid();
@@ -54,6 +54,6 @@ int rmc_dismount(
 
         while ((c = send2rmc (server, sendbuf, msglen, repbuf, sizeof(repbuf))) &&
             serrno == ERMCNACT)
-                sleep (RETRYI);
+                sleep (RMC_RETRYI);
 	return (c);
 }
diff --git a/rmc/rmc_errmsg.c b/rmc/rmc_errmsg.c
index d4f60c56599f8d47c174a846e2fcbc8822b9d06a..9c2342810bf738376b3961fe0a0f6851f59b7e12 100644
--- a/rmc/rmc_errmsg.c
+++ b/rmc/rmc_errmsg.c
@@ -8,7 +8,8 @@
 #include <string.h>
 #include <stdarg.h>
 #include <sys/types.h>
-#include "rmc.h"
+
+#include "h/rmc_constants.h"
 
 static char *errbufp = NULL;
 static int errbuflen;
@@ -28,7 +29,7 @@ rmc_seterrbuf(char *buffer,
 int rmc_errmsg(char *func, char *msg, ...)
 {
 	va_list args;
-	char prtbuf[PRTBUFSZ];
+	char prtbuf[RMC_PRTBUFSZ];
 	int save_errno;
 
 	save_errno = errno;
diff --git a/rmc/rmc_export.c b/rmc/rmc_export.c
index 38914793e10c1d2ead757b6c95f24b7d16314e0d..08bc1ccd03eae32eab5c8e4cdfc48ef761b80961 100644
--- a/rmc/rmc_export.c
+++ b/rmc/rmc_export.c
@@ -8,10 +8,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <netinet/in.h>
-#include "marshall.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/marshall.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 int rmc_export(const char *const server, const char *const vid)
 {
 	int c;
@@ -20,7 +20,7 @@ int rmc_export(const char *const server, const char *const vid)
 	char *q;
 	char repbuf[1];
 	char *sbp;
-	char sendbuf[REQBUFSZ];
+	char sendbuf[RMC_REQBUFSZ];
 	uid_t uid;
 
 	uid = getuid();
@@ -47,6 +47,6 @@ int rmc_export(const char *const server, const char *const vid)
 
         while ((c = send2rmc (server, sendbuf, msglen, repbuf, sizeof(repbuf))) &&
             serrno == ERMCNACT)
-                sleep (RETRYI);
+                sleep (RMC_RETRYI);
 	return (c);
 }
diff --git a/rmc/rmc_find_cartridge.c b/rmc/rmc_find_cartridge.c
index 9497a1ac36379f3a4ba45a7995b58242200f58b5..8728ba2f4e1dd0285d06f85beaceb08896ddf12c 100644
--- a/rmc/rmc_find_cartridge.c
+++ b/rmc/rmc_find_cartridge.c
@@ -9,10 +9,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <netinet/in.h>
-#include "marshall.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/marshall.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 int rmc_find_cartridge(
 	const char *const server,
 	const char *const pattern,
@@ -28,9 +28,9 @@ int rmc_find_cartridge(
 	int msglen;
 	char *q;
 	char *rbp;
-	char repbuf[REPBUFSZ];
+	char repbuf[RMC_REPBUFSZ];
 	char *sbp;
-	char sendbuf[REQBUFSZ];
+	char sendbuf[RMC_REQBUFSZ];
 	uid_t uid;
 
 	uid = getuid();
@@ -60,7 +60,7 @@ int rmc_find_cartridge(
 
         while ((c = send2rmc (server, sendbuf, msglen, repbuf, sizeof(repbuf))) &&
             serrno == ERMCNACT)
-                sleep (RETRYI);
+                sleep (RMC_RETRYI);
 	if (c == 0) {
 		rbp = repbuf;
 		unmarshall_LONG (rbp, c);
diff --git a/rmc/rmc_get_geometry.c b/rmc/rmc_get_geometry.c
index cbf23df57920dbcd927127c5b61814b3448045c3..16f7e42f5c57ed7f94ae8636bcd92aac904593e4 100644
--- a/rmc/rmc_get_geometry.c
+++ b/rmc/rmc_get_geometry.c
@@ -9,10 +9,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <netinet/in.h>
-#include "marshall.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/marshall.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 int rmc_get_geometry(
 	const char *const server,
 	struct robot_info *const robot_info)
@@ -24,7 +24,7 @@ int rmc_get_geometry(
 	char *rbp;
 	char repbuf[64];
 	char *sbp;
-	char sendbuf[REQBUFSZ];
+	char sendbuf[RMC_REQBUFSZ];
 	uid_t uid;
 
 	uid = getuid();
@@ -50,7 +50,7 @@ int rmc_get_geometry(
 
         while ((c = send2rmc (server, sendbuf, msglen, repbuf, sizeof(repbuf))) &&
             serrno == ERMCNACT)
-                sleep (RETRYI);
+                sleep (RMC_RETRYI);
 	if (c == 0) {
 		rbp = repbuf;
         	unmarshall_STRING (rbp, robot_info->inquiry);
diff --git a/rmc/rmc_import.c b/rmc/rmc_import.c
index c198b2868ae50516273c8f292bb0c55dde07e8f8..bc0d20e3ab2ba984a4ad2c6f65f004105d86d379 100644
--- a/rmc/rmc_import.c
+++ b/rmc/rmc_import.c
@@ -9,10 +9,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <netinet/in.h>
-#include "marshall.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/marshall.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 int rmc_import(const char *const server, const char *const vid)
 {
 	int c;
@@ -21,7 +21,7 @@ int rmc_import(const char *const server, const char *const vid)
 	char *q;
 	char repbuf[1];
 	char *sbp;
-	char sendbuf[REQBUFSZ];
+	char sendbuf[RMC_REQBUFSZ];
 	uid_t uid;
 
 	uid = getuid();
@@ -48,6 +48,6 @@ int rmc_import(const char *const server, const char *const vid)
 
         while ((c = send2rmc (server, sendbuf, msglen, repbuf, sizeof(repbuf))) &&
             serrno == ERMCNACT)
-                sleep (RETRYI);
+                sleep (RMC_RETRYI);
 	return (c);
 }
diff --git a/rmc/rmclogit.c b/rmc/rmc_logit.c
similarity index 85%
rename from rmc/rmclogit.c
rename to rmc/rmc_logit.c
index dd93368c7fdb1a9beff550822eb90ab384c10481..76054ebbaa593bccc74ec6c1c7a20277fa2c92bd 100644
--- a/rmc/rmclogit.c
+++ b/rmc/rmc_logit.c
@@ -11,13 +11,14 @@
 #include <time.h>
 #include <stdarg.h>
 #include <unistd.h>
-#include "rmc.h"
+#include "h/rmc_constants.h"
+#include "h/rmc_logit.h"
 extern int jid;
 
-int rmclogit(char *func, char *msg, ...)
+int rmc_logit(const char *const func, const char *const msg, ...)
 {
 	va_list args;
-	char prtbuf[PRTBUFSZ];
+	char prtbuf[RMC_PRTBUFSZ];
 	int save_errno;
 	struct tm *tm;
 	time_t current_time;
diff --git a/rmc/rmc_logreq.c b/rmc/rmc_logreq.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ee62b40d8f1afff89a104c86586be7bbfb17894
--- /dev/null
+++ b/rmc/rmc_logreq.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2001-2002 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+ 
+#include "h/rmc_constants.h"
+#include "h/rmc_logit.h"
+#include "h/rmc_logreq.h"
+#include "h/tplogger_api.h"
+
+#include <string.h>
+ 
+/*	rmc_logreq - log a request */
+
+/*	Split the message into lines so they don't exceed LOGBUFSZ-1 characters
+ *	A backslash is appended to a line to be continued
+ *	A continuation line is prefixed by '+ '
+ */
+void rmc_logreq(const char *const func, char *const logbuf) {
+	int n1, n2;
+	char *p;
+	char savechrs1[2];
+	char savechrs2[2];
+
+	n1 = RMC_LOGBUFSZ - strlen (func) - 36;
+	n2 = strlen (logbuf);
+	p = logbuf;
+	while (n2 > n1) {
+		savechrs1[0] = *(p + n1);
+		savechrs1[1] = *(p + n1 + 1);
+		*(p + n1) = '\\';
+		*(p + n1 + 1) = '\0';
+		rmc_logit (func, RMC98, p);
+                tl_rmcdaemon.tl_log( &tl_rmcdaemon, 98, 2,
+                                     "func"   , TL_MSG_PARAM_STR, "rmc_logreq",
+                                     "Request", TL_MSG_PARAM_STR, p );
+		if (p != logbuf) {
+			*p = savechrs2[0];
+			*(p + 1) = savechrs2[1];
+		}
+		p += n1 - 2;
+		savechrs2[0] = *p;
+		savechrs2[1] = *(p + 1);
+		*p = '+';
+		*(p + 1) = ' ';
+		*(p + 2) = savechrs1[0];
+		*(p + 3) = savechrs1[1];
+		n2 -= n1;
+	}
+	rmc_logit (func, RMC98, p);
+        tl_rmcdaemon.tl_log( &tl_rmcdaemon, 98, 2,
+                             "func"   , TL_MSG_PARAM_STR, "rmc_logreq",
+                             "Request", TL_MSG_PARAM_STR, p );
+	if (p != logbuf) {
+		*p = savechrs2[0];
+		*(p + 1) = savechrs2[1];
+	}
+}
diff --git a/rmc/rmc_marshall_element.c b/rmc/rmc_marshall_element.c
new file mode 100644
index 0000000000000000000000000000000000000000..f1607fcce2debc6e1546e534c2b5e011ffe04757
--- /dev/null
+++ b/rmc/rmc_marshall_element.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2001-2002 by CERN/IT/PDP/DM
+ * All rights reserved
+ */
+ 
+#include "h/marshall.h"
+#include "h/rmc_marshall_element.h"
+ 
+int rmc_marshall_element (
+	char **const sbpp,
+	const struct smc_element_info *const element_info)
+{
+	char *sbp = *sbpp;
+
+	marshall_WORD (sbp, element_info->element_address);
+	marshall_BYTE (sbp, element_info->element_type);
+	marshall_BYTE (sbp, element_info->state);
+	marshall_BYTE (sbp, element_info->asc);
+	marshall_BYTE (sbp, element_info->ascq);
+	marshall_BYTE (sbp, element_info->flags);
+	marshall_WORD (sbp, element_info->source_address);
+	marshall_STRING (sbp, element_info->name);
+	*sbpp = sbp;
+	return (0);
+}
diff --git a/rmc/rmc_mount.c b/rmc/rmc_mount.c
index f1dd077aac53725769ad4f5dde93cef4a31fadaa..aa72260c325419e5dac143eca669ec7383e84b87 100644
--- a/rmc/rmc_mount.c
+++ b/rmc/rmc_mount.c
@@ -9,10 +9,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <netinet/in.h>
-#include "marshall.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/marshall.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 int rmc_mount(
 	const char *const server,
 	const char *const vid,
@@ -25,7 +25,7 @@ int rmc_mount(
 	char *q;
 	char repbuf[1];
 	char *sbp;
-	char sendbuf[REQBUFSZ];
+	char sendbuf[RMC_REQBUFSZ];
 	uid_t uid;
 
 	uid = getuid();
@@ -54,6 +54,6 @@ int rmc_mount(
 
         while ((c = send2rmc (server, sendbuf, msglen, repbuf, sizeof(repbuf))) &&
             serrno == ERMCNACT)
-                sleep (RETRYI);
+                sleep (RMC_RETRYI);
 	return (c);
 }
diff --git a/rmc/rmc_procreq.c b/rmc/rmc_procreq.c
index 4cda2a7acb644fb10865988725868ec70e13f00e..5390ea0483832690247d6197cd0d830847818fa2 100644
--- a/rmc/rmc_procreq.c
+++ b/rmc/rmc_procreq.c
@@ -12,91 +12,25 @@
 #include <time.h>
 #include <sys/types.h>
 #include <netinet/in.h>
-#include "Cupv_api.h"
-#include "marshall.h"
-#include "rmc.h"
-#include "serrno.h"
-#include "tplogger_api.h"
+#include "h/Cupv_api.h"
+#include "h/marshall.h"
+#include "h/serrno.h"
+#include "h/rmc_constants.h"
+#include "h/rmc_logit.h"
+#include "h/rmc_logreq.h"
+#include "h/rmc_marshall_element.h"
+#include "h/rmc_procreq.h"
+#include "h/rmc_smcsubr.h"
+#include "h/rmc_smcsubr2.h"
+#include "h/rmc_sendrep.h"
+#include "h/tplogger_api.h"
 #include <string.h>
 #include <Ctape_api.h>
-extern int being_shutdown;
 extern struct extended_robot_info extended_robot_info;
-extern char localhost[CA_MAXHOSTNAMELEN+1];
-extern int rpfd;
-void procreq(int, char*, char*);
  
-/*	rmc_logreq - log a request */
-
-/*	Split the message into lines so they don't exceed LOGBUFSZ-1 characters
- *	A backslash is appended to a line to be continued
- *	A continuation line is prefixed by '+ '
- */
-void
-rmc_logreq(char *func,
-	   char *logbuf)
-{
-	int n1, n2;
-	char *p;
-	char savechrs1[2];
-	char savechrs2[2];
-
-	n1 = LOGBUFSZ - strlen (func) - 36;
-	n2 = strlen (logbuf);
-	p = logbuf;
-	while (n2 > n1) {
-		savechrs1[0] = *(p + n1);
-		savechrs1[1] = *(p + n1 + 1);
-		*(p + n1) = '\\';
-		*(p + n1 + 1) = '\0';
-		rmclogit (func, RMC98, p);
-                tl_rmcdaemon.tl_log( &tl_rmcdaemon, 98, 2,
-                                     "func"   , TL_MSG_PARAM_STR, "rmc_logreq",
-                                     "Request", TL_MSG_PARAM_STR, p );
-		if (p != logbuf) {
-			*p = savechrs2[0];
-			*(p + 1) = savechrs2[1];
-		}
-		p += n1 - 2;
-		savechrs2[0] = *p;
-		savechrs2[1] = *(p + 1);
-		*p = '+';
-		*(p + 1) = ' ';
-		*(p + 2) = savechrs1[0];
-		*(p + 3) = savechrs1[1];
-		n2 -= n1;
-	}
-	rmclogit (func, RMC98, p);
-        tl_rmcdaemon.tl_log( &tl_rmcdaemon, 98, 2,
-                             "func"   , TL_MSG_PARAM_STR, "rmc_logreq",
-                             "Request", TL_MSG_PARAM_STR, p );
-	if (p != logbuf) {
-		*p = savechrs2[0];
-		*(p + 1) = savechrs2[1];
-	}
-}
-
-int marshall_ELEMENT (char **sbpp,
-                      struct smc_element_info *element_info)
-{
-	char *sbp = *sbpp;
-
-	marshall_WORD (sbp, element_info->element_address);
-	marshall_BYTE (sbp, element_info->element_type);
-	marshall_BYTE (sbp, element_info->state);
-	marshall_BYTE (sbp, element_info->asc);
-	marshall_BYTE (sbp, element_info->ascq);
-	marshall_BYTE (sbp, element_info->flags);
-	marshall_WORD (sbp, element_info->source_address);
-	marshall_STRING (sbp, element_info->name);
-	*sbpp = sbp;
-	return (0);
-}
-
 /*	rmc_srv_export - export/eject a cartridge from the robot */
 
-int rmc_srv_export(char *req_data,
-                   char *clienthost)
-{
+int rmc_srv_export(struct rmc_srv_rqst_context *const rqst_context) {
 	int c;
 	char func[16];
 	gid_t gid;
@@ -106,46 +40,51 @@ int rmc_srv_export(char *req_data,
 	char vid[CA_MAXVIDLEN+1];
 
 	strncpy (func, "rmc_srv_export", 16);
-	rbp = req_data;
+	rbp = rqst_context->req_data;
 	unmarshall_LONG (rbp, uid);
 	unmarshall_LONG (rbp, gid);
-	rmclogit (func, RMC92, "export", uid, gid, clienthost);
+	rmc_logit (func, RMC92, "export", uid, gid, rqst_context->clienthost);
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 92, 5,
-                             "func"      , TL_MSG_PARAM_STR, "rmc_srv_export",
-                             "Type"      , TL_MSG_PARAM_STR, "export",
-                             "UID"       , TL_MSG_PARAM_UID, uid,
-                             "GID"       , TL_MSG_PARAM_GID, gid,
-                             "ClientHost", TL_MSG_PARAM_STR, clienthost );
+		"func"      , TL_MSG_PARAM_STR, "rmc_srv_export",
+		"Type"      , TL_MSG_PARAM_STR, "export",
+		"UID"       , TL_MSG_PARAM_UID, uid,
+		"GID"       , TL_MSG_PARAM_GID, gid,
+		"ClientHost", TL_MSG_PARAM_STR, rqst_context->clienthost );
 	/* Unmarshall and ignore the loader field as it is no longer used */
 	{
 		char smc_ldr[CA_MAXRBTNAMELEN+1];
 		if (unmarshall_STRINGN (rbp, smc_ldr, CA_MAXRBTNAMELEN+1)) {
-			sendrep (rpfd, MSG_ERR, RMC06, "loader");
-			RETURN (ERMCUNREC);
+			rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06,
+				"loader");
+			rmc_logit (func, "returns %d\n", ERMCUNREC);
+			return ERMCUNREC;
 		}
 	}
 	if (unmarshall_STRINGN (rbp, vid, CA_MAXVIDLEN+1)) {
-		sendrep (rpfd, MSG_ERR, RMC06, "vid");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "vid");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	snprintf (logbuf, CA_MAXVIDLEN+8, "export %s", vid);
 	rmc_logreq (func, logbuf);
 
-	if (Cupv_check (uid, gid, clienthost, localhost, P_TAPE_OPERATOR)) {
-		sendrep (rpfd, MSG_ERR, "%s\n", sstrerror(serrno));
-		RETURN (ERMCUNREC);
+	if (Cupv_check (uid, gid, rqst_context->clienthost,
+		rqst_context->localhost, P_TAPE_OPERATOR)) {
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, "%s\n",
+			sstrerror(serrno));
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
-	c = smc_export (extended_robot_info.smc_fd, extended_robot_info.smc_ldr,
-	    &extended_robot_info.robot_info, vid);
+	c = smc_export (rqst_context->rpfd, extended_robot_info.smc_fd,
+          extended_robot_info.smc_ldr, &extended_robot_info.robot_info, vid);
 	if (c) c += ERMCRBTERR;
-	RETURN (c);
+	rmc_logit (func, "returns %d\n", c);
+	return c;
 }
 
 /*	rmc_srv_findcart - find cartridge(s) */
 
-int rmc_srv_findcart(char *req_data,
-                     char *clienthost)
-{
+int rmc_srv_findcart(struct rmc_srv_rqst_context *const rqst_context) {
 	int c;
 	struct smc_element_info *element_info;
 	struct smc_element_info *elemp;
@@ -165,27 +104,31 @@ int rmc_srv_findcart(char *req_data,
 	uid_t uid;
 
 	strncpy (func, "rmc_srv_findcart", 17);
-	rbp = req_data;
+	rbp = rqst_context->req_data;
 	unmarshall_LONG (rbp, uid);
 	unmarshall_LONG (rbp, gid);
-	rmclogit (func, RMC92, "findcart", uid, gid, clienthost);
+	rmc_logit (func, RMC92, "findcart", uid, gid, rqst_context->clienthost);
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 92, 5,
-                             "func"      , TL_MSG_PARAM_STR, "rmc_srv_findcart",
-                             "Type"      , TL_MSG_PARAM_STR, "findcart",
-                             "UID"       , TL_MSG_PARAM_UID, uid,
-                             "GID"       , TL_MSG_PARAM_GID, gid,
-                             "ClientHost", TL_MSG_PARAM_STR, clienthost );
+		"func"      , TL_MSG_PARAM_STR, "rmc_srv_findcart",
+		"Type"      , TL_MSG_PARAM_STR, "findcart",
+		"UID"       , TL_MSG_PARAM_UID, uid,
+		"GID"       , TL_MSG_PARAM_GID, gid,
+		"ClientHost", TL_MSG_PARAM_STR,
+		rqst_context->clienthost);
 	/* Unmarshall and ignore the loader fiel as it is no longer used */
 	{
 		char smc_ldr[CA_MAXRBTNAMELEN+1];
 		if (unmarshall_STRINGN (rbp, smc_ldr, CA_MAXRBTNAMELEN+1)) {
-			sendrep (rpfd, MSG_ERR, RMC06, "loader");
-			RETURN (ERMCUNREC);
+			rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06,
+				"loader");
+			rmc_logit (func, "returns %d\n", ERMCUNREC);
+			return ERMCUNREC;
 		}
 	}
 	if (unmarshall_STRINGN (rbp, template, 40)) {
-		sendrep (rpfd, MSG_ERR, RMC06, "template");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "template");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	unmarshall_LONG (rbp, type);
 	unmarshall_LONG (rbp, startaddr);
@@ -194,12 +137,14 @@ int rmc_srv_findcart(char *req_data,
 	rmc_logreq (func, logbuf);
 
 	if (nbelem < 1) {
-		sendrep (rpfd, MSG_ERR, RMC06, "nbelem");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "nbelem");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	if ((element_info = malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
-		sendrep (rpfd, MSG_ERR, RMC05);
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC05);
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	if (extended_robot_info.smc_support_voltag)
 		c = smc_find_cartridge (extended_robot_info.smc_fd,
@@ -212,30 +157,32 @@ int rmc_srv_findcart(char *req_data,
 	if (c < 0) {
 		c = smc_lasterror (&smc_status, &msgaddr);
 		free (element_info);
-		sendrep (rpfd, MSG_ERR, RMC02, "smc_find_cartridge", msgaddr);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC02,
+			"smc_find_cartridge", msgaddr);
 		c += ERMCRBTERR;
-		RETURN (c);
+		rmc_logit (func, "returns %d\n", c);
+		return c;
 	}
 	if ((repbuf = malloc (c * 18 + 4)) == NULL) {
-		sendrep (rpfd, MSG_ERR, RMC05);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC05);
 		free (element_info);
-		RETURN (ERMCUNREC);
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	sbp = repbuf;
 	marshall_LONG (sbp, c);
 	for (i = 0, elemp = element_info; i < c; i++, elemp++)
-		marshall_ELEMENT (&sbp, elemp);
+		rmc_marshall_element (&sbp, elemp);
 	free (element_info);
-	sendrep (rpfd, MSG_DATA, sbp - repbuf, repbuf);
+	rmc_sendrep (rqst_context->rpfd, MSG_DATA, sbp - repbuf, repbuf);
 	free (repbuf);
-	RETURN (0);
+	rmc_logit (func, "returns %d\n", 0);
+	return 0;
 }
 
 /*	rmc_srv_getgeom - get the robot geometry */
 
-int rmc_srv_getgeom(char *req_data,
-                    char *clienthost)
-{
+int rmc_srv_getgeom(struct rmc_srv_rqst_context *const rqst_context) {
 	char func[16];
 	gid_t gid;
 	char logbuf[8];
@@ -245,22 +192,24 @@ int rmc_srv_getgeom(char *req_data,
 	uid_t uid;
 
 	strncpy (func, "rmc_srv_getgeom", 16);
-	rbp = req_data;
+	rbp = rqst_context->req_data;
 	unmarshall_LONG (rbp, uid);
 	unmarshall_LONG (rbp, gid);
-	rmclogit (func, RMC92, "getgeom", uid, gid, clienthost);
+	rmc_logit (func, RMC92, "getgeom", uid, gid, rqst_context->clienthost);
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 92, 5,
-                             "func"      , TL_MSG_PARAM_STR, "rmc_srv_getgeom",
-                             "Type"      , TL_MSG_PARAM_STR, "getgeom",
-                             "UID"       , TL_MSG_PARAM_UID, uid,
-                             "GID"       , TL_MSG_PARAM_GID, gid,
-                             "ClientHost", TL_MSG_PARAM_STR, clienthost );
+		"func"      , TL_MSG_PARAM_STR, "rmc_srv_getgeom",
+		"Type"      , TL_MSG_PARAM_STR, "getgeom",
+		"UID"       , TL_MSG_PARAM_UID, uid,
+		"GID"       , TL_MSG_PARAM_GID, gid,
+		"ClientHost", TL_MSG_PARAM_STR, rqst_context->clienthost );
 	/* Unmarshall and ignore the loader field as it is no longer used */
 	{
 		char smc_ldr[CA_MAXRBTNAMELEN+1];
 		if (unmarshall_STRINGN (rbp, smc_ldr, CA_MAXRBTNAMELEN+1)) {
-			sendrep (rpfd, MSG_ERR, RMC06, "loader");
-			RETURN (ERMCUNREC);
+			rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06,
+				"loader");
+			rmc_logit (func, "returns %d\n", ERMCUNREC);
+			return ERMCUNREC;
 		}
 	}
 	snprintf (logbuf, 8, "getgeom");
@@ -276,15 +225,14 @@ int rmc_srv_getgeom(char *req_data,
 	marshall_LONG (sbp, extended_robot_info.robot_info.port_count);
 	marshall_LONG (sbp, extended_robot_info.robot_info.device_start);
 	marshall_LONG (sbp, extended_robot_info.robot_info.device_count);
-	sendrep (rpfd, MSG_DATA, sbp - repbuf, repbuf);
-	RETURN (0);
+	rmc_sendrep (rqst_context->rpfd, MSG_DATA, sbp - repbuf, repbuf);
+	rmc_logit (func, "returns %d\n", 0);
+	return 0;
 }
 
 /*	rmc_srv_import - import/inject a cartridge into the robot */
 
-int rmc_srv_import(char *req_data,
-                   char *clienthost)
-{
+int rmc_srv_import(struct rmc_srv_rqst_context *const rqst_context) {
 	int c;
 	char func[16];
 	gid_t gid;
@@ -294,46 +242,51 @@ int rmc_srv_import(char *req_data,
 	char vid[CA_MAXVIDLEN+1];
 
 	strncpy (func, "rmc_srv_import", 16);
-	rbp = req_data;
+	rbp = rqst_context->req_data;
 	unmarshall_LONG (rbp, uid);
 	unmarshall_LONG (rbp, gid);
-	rmclogit (func, RMC92, "import", uid, gid, clienthost);
+	rmc_logit (func, RMC92, "import", uid, gid, rqst_context->clienthost);
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 92, 5,
-                             "func"      , TL_MSG_PARAM_STR, "rmc_srv_import",
-                             "Type"      , TL_MSG_PARAM_STR, "import",
-                             "UID"       , TL_MSG_PARAM_UID, uid,
-                             "GID"       , TL_MSG_PARAM_GID, gid,
-                             "ClientHost", TL_MSG_PARAM_STR, clienthost );
+		"func"      , TL_MSG_PARAM_STR, "rmc_srv_import",
+		"Type"      , TL_MSG_PARAM_STR, "import",
+		"UID"       , TL_MSG_PARAM_UID, uid,
+		"GID"       , TL_MSG_PARAM_GID, gid,
+		"ClientHost", TL_MSG_PARAM_STR, rqst_context->clienthost );
 	/* Unmarshall and ignore the loader field as it is no longer used */
 	{
 		char smc_ldr[CA_MAXRBTNAMELEN+1];
 		if (unmarshall_STRINGN (rbp, smc_ldr, CA_MAXRBTNAMELEN+1)) {
-			sendrep (rpfd, MSG_ERR, RMC06, "loader");
-			RETURN (ERMCUNREC);
+			rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06,
+				"loader");
+			rmc_logit (func, "returns %d\n", ERMCUNREC);
+			return ERMCUNREC;
 		}
 	}
 	if (unmarshall_STRINGN (rbp, vid, CA_MAXVIDLEN+1)) {
-		sendrep (rpfd, MSG_ERR, RMC06, "vid");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "vid");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	snprintf (logbuf, CA_MAXVIDLEN+8, "import %s", vid);
 	rmc_logreq (func, logbuf);
 
-	if (Cupv_check (uid, gid, clienthost, localhost, P_TAPE_OPERATOR)) {
-		sendrep (rpfd, MSG_ERR, "%s\n", sstrerror(serrno));
-		RETURN (ERMCUNREC);
+	if (Cupv_check (uid, gid, rqst_context->clienthost,
+		rqst_context->localhost, P_TAPE_OPERATOR)) {
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, "%s\n",
+			sstrerror(serrno));
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
-	c = smc_import (extended_robot_info.smc_fd, extended_robot_info.smc_ldr,
-	    &extended_robot_info.robot_info, vid);
+	c = smc_import (rqst_context->rpfd, extended_robot_info.smc_fd,
+	  extended_robot_info.smc_ldr, &extended_robot_info.robot_info, vid);
 	if (c) c += ERMCRBTERR;
-	RETURN (c);
+	rmc_logit (func, "returns %d\n", c);
+	return c;
 }
 
 /*	rmc_srv_mount - mount a cartridge on a drive */
 
-int rmc_srv_mount(char *req_data,
-                  char *clienthost)
-{
+int rmc_srv_mount(struct rmc_srv_rqst_context *const rqst_context) {
 	int c;
 	int drvord;
 	char func[16];
@@ -345,48 +298,54 @@ int rmc_srv_mount(char *req_data,
 	char vid[CA_MAXVIDLEN+1];
 
 	strncpy (func, "rmc_srv_mount", 16);
-	rbp = req_data;
+	rbp = rqst_context->req_data;
 	unmarshall_LONG (rbp, uid);
 	unmarshall_LONG (rbp, gid);
-	rmclogit (func, RMC92, "mount", uid, gid, clienthost);
+	rmc_logit (func, RMC92, "mount", uid, gid, rqst_context->clienthost);
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 92, 5,
-                             "func"      , TL_MSG_PARAM_STR, "rmc_srv_mount",
-                             "Type"      , TL_MSG_PARAM_STR, "mount",
-                             "UID"       , TL_MSG_PARAM_UID, uid,
-                             "GID"       , TL_MSG_PARAM_GID, gid,
-                             "ClientHost", TL_MSG_PARAM_STR, clienthost );
+		"func"      , TL_MSG_PARAM_STR, "rmc_srv_mount",
+		"Type"      , TL_MSG_PARAM_STR, "mount",
+		"UID"       , TL_MSG_PARAM_UID, uid,
+		"GID"       , TL_MSG_PARAM_GID, gid,
+		"ClientHost", TL_MSG_PARAM_STR, rqst_context->clienthost );
 	/* Unmarshall and ignore the loader field as it is no longer used */
 	{
 		char smc_ldr[CA_MAXRBTNAMELEN+1];
 		if (unmarshall_STRINGN (rbp, smc_ldr, CA_MAXRBTNAMELEN+1)) {
-			sendrep (rpfd, MSG_ERR, RMC06, "loader");
-			RETURN (ERMCUNREC);
+			rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06,
+				"loader");
+			rmc_logit (func, "returns %d\n", ERMCUNREC);
+			return ERMCUNREC;
 		}
 	}
 	if (unmarshall_STRINGN (rbp, vid, CA_MAXVIDLEN+1)) {
-		sendrep (rpfd, MSG_ERR, RMC06, "vid");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "vid");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	unmarshall_WORD (rbp, invert);
 	unmarshall_WORD (rbp, drvord);
 	snprintf (logbuf, CA_MAXVIDLEN+64, "mount %s/%d on drive %d", vid, invert, drvord);
 	rmc_logreq (func, logbuf);
 
-	if (Cupv_check (uid, gid, clienthost, localhost, P_TAPE_SYSTEM)) {
-		sendrep (rpfd, MSG_ERR, "%s\n", sstrerror(serrno));
-		RETURN (ERMCUNREC);
+	if (Cupv_check (uid, gid, rqst_context->clienthost,
+		rqst_context->localhost, P_TAPE_SYSTEM)) {
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, "%s\n",
+			sstrerror(serrno));
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
-	c = smc_mount (extended_robot_info.smc_fd, extended_robot_info.smc_ldr,
-	    &extended_robot_info.robot_info, drvord, vid, invert);
+	c = smc_mount (rqst_context->rpfd, extended_robot_info.smc_fd,
+	  extended_robot_info.smc_ldr, &extended_robot_info.robot_info, drvord,
+	  vid, invert);
 	if (c) c += ERMCRBTERR;
-	RETURN (c);
+	rmc_logit (func, "returns %d\n", c);
+	return c;
 }
 
 /*	rmc_srv_readelem - read element status */
 
-int rmc_srv_readelem(char *req_data,
-                     char *clienthost)
-{
+int rmc_srv_readelem(struct rmc_srv_rqst_context *const rqst_context) {
 	int c;
 	struct smc_element_info *element_info;
 	struct smc_element_info *elemp;
@@ -405,22 +364,24 @@ int rmc_srv_readelem(char *req_data,
 	uid_t uid;
 
 	strncpy (func, "rmc_srv_readelem", 17);
-	rbp = req_data;
+	rbp = rqst_context->req_data;
 	unmarshall_LONG (rbp, uid);
 	unmarshall_LONG (rbp, gid);
-	rmclogit (func, RMC92, "readelem", uid, gid, clienthost);
+	rmc_logit (func, RMC92, "readelem", uid, gid, rqst_context->clienthost);
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 92, 5,
-                             "func"      , TL_MSG_PARAM_STR, "rmc_srv_readelem",
-                             "Type"      , TL_MSG_PARAM_STR, "readelem",
-                             "UID"       , TL_MSG_PARAM_UID, uid,
-                             "GID"       , TL_MSG_PARAM_GID, gid,
-                             "ClientHost", TL_MSG_PARAM_STR, clienthost );
+		"func"      , TL_MSG_PARAM_STR, "rmc_srv_readelem",
+		"Type"      , TL_MSG_PARAM_STR, "readelem",
+		"UID"       , TL_MSG_PARAM_UID, uid,
+		"GID"       , TL_MSG_PARAM_GID, gid,
+		"ClientHost", TL_MSG_PARAM_STR, rqst_context->clienthost );
 	/* Unmarshall and ignore the loader field as it is no longer used */
 	{
 		char smc_ldr[CA_MAXRBTNAMELEN+1];
 		if (unmarshall_STRINGN (rbp, smc_ldr, CA_MAXRBTNAMELEN+1)) {
-			sendrep (rpfd, MSG_ERR, RMC06, "loader");
-			RETURN (ERMCUNREC);
+			rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06,
+				"loader");
+			rmc_logit (func, "returns %d\n", ERMCUNREC);
+			return ERMCUNREC;
 		}
 	}
 	unmarshall_LONG (rbp, type);
@@ -430,46 +391,51 @@ int rmc_srv_readelem(char *req_data,
 	rmc_logreq (func, logbuf);
 
 	if (type < 0 || type > 4) {
-		sendrep (rpfd, MSG_ERR, RMC06, "type");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "type");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	if (nbelem < 1) {
-		sendrep (rpfd, MSG_ERR, RMC06, "nbelem");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "nbelem");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	if ((element_info = malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
-		sendrep (rpfd, MSG_ERR, RMC05);
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC05);
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	if ((c = smc_read_elem_status (extended_robot_info.smc_fd,
 	    extended_robot_info.smc_ldr, type, startaddr, nbelem,
 	    element_info)) < 0) {
 		c = smc_lasterror (&smc_status, &msgaddr);
 		free (element_info);
-		sendrep (rpfd, MSG_ERR, RMC02, "smc_read_elem_status", msgaddr);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC02,
+			"smc_read_elem_status", msgaddr);
 		c += ERMCRBTERR;
-		RETURN (c);
+		rmc_logit (func, "returns %d\n", c);
+		return c;
 	}
 	if ((repbuf = malloc (c * 18 + 4)) == NULL) {
-		sendrep (rpfd, MSG_ERR, RMC05);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC05);
 		free (element_info);
-		RETURN (ERMCUNREC);
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	sbp = repbuf;
 	marshall_LONG (sbp, c);
 	for (i = 0, elemp = element_info; i < c; i++, elemp++)
-		marshall_ELEMENT (&sbp, elemp);
+		rmc_marshall_element (&sbp, elemp);
 	free (element_info);
-	sendrep (rpfd, MSG_DATA, sbp - repbuf, repbuf);
+	rmc_sendrep (rqst_context->rpfd, MSG_DATA, sbp - repbuf, repbuf);
 	free (repbuf);
-	RETURN (0);
+	rmc_logit (func, "returns %d\n", 0);
+	return 0;
 }
 
 /*	rmc_srv_unmount - dismount a cartridge from a drive */
 
-int rmc_srv_unmount(char *req_data,
-                    char *clienthost)
-{
+int rmc_srv_unmount(struct rmc_srv_rqst_context *const rqst_context) {
 	int c;
 	int drvord;
 	int force;
@@ -481,39 +447,55 @@ int rmc_srv_unmount(char *req_data,
 	char vid[CA_MAXVIDLEN+1];
 
 	strncpy (func, "rmc_srv_unmount", 16);
-	rbp = req_data;
+	rbp = rqst_context->req_data;
 	unmarshall_LONG (rbp, uid);
 	unmarshall_LONG (rbp, gid);
-	rmclogit (func, RMC92, "unmount", uid, gid, clienthost);
+	rmc_logit (func, RMC92, "unmount", uid, gid, rqst_context->clienthost);
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 92, 5,
-                             "func"      , TL_MSG_PARAM_STR, "rmc_srv_unmount",
-                             "Type"      , TL_MSG_PARAM_STR, "unmount",
-                             "UID"       , TL_MSG_PARAM_UID, uid,
-                             "GID"       , TL_MSG_PARAM_GID, gid,
-                             "ClientHost", TL_MSG_PARAM_STR, clienthost );
+		"func"      , TL_MSG_PARAM_STR, "rmc_srv_unmount",
+		"Type"      , TL_MSG_PARAM_STR, "unmount",
+		"UID"       , TL_MSG_PARAM_UID, uid,
+		"GID"       , TL_MSG_PARAM_GID, gid,
+		"ClientHost", TL_MSG_PARAM_STR, rqst_context->clienthost );
 	/* Unmarshall and ignore the loader field as it is no longer used */
 	{
 		char smc_ldr[CA_MAXRBTNAMELEN+1];
 		if (unmarshall_STRINGN (rbp, smc_ldr, CA_MAXRBTNAMELEN+1)) {
-			sendrep (rpfd, MSG_ERR, RMC06, "loader");
-			RETURN (ERMCUNREC);
+			rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06,
+				"loader");
+			rmc_logit (func, "returns %d\n", ERMCUNREC);
+			return ERMCUNREC;
 		}
 	}
 	if (unmarshall_STRINGN (rbp, vid, CA_MAXVIDLEN+1)) {
-		sendrep (rpfd, MSG_ERR, RMC06, "vid");
-		RETURN (ERMCUNREC);
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, RMC06, "vid");
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
 	unmarshall_WORD (rbp, drvord);
 	unmarshall_WORD (rbp, force);
 	snprintf (logbuf, CA_MAXVIDLEN+64, "unmount %s %d %d", vid, drvord, force);
 	rmc_logreq (func, logbuf);
 
-	if (Cupv_check (uid, gid, clienthost, localhost, P_TAPE_SYSTEM)) {
-		sendrep (rpfd, MSG_ERR, "%s\n", sstrerror(serrno));
-		RETURN (ERMCUNREC);
+	if (Cupv_check (uid, gid, rqst_context->clienthost,
+		rqst_context->localhost, P_TAPE_SYSTEM)) {
+		rmc_sendrep (rqst_context->rpfd, MSG_ERR, "%s\n",
+			sstrerror(serrno));
+		rmc_logit (func, "returns %d\n", ERMCUNREC);
+		return ERMCUNREC;
 	}
-	c = smc_dismount (extended_robot_info.smc_fd, extended_robot_info.smc_ldr,
-	    &extended_robot_info.robot_info, drvord, force == 0 ? vid : "");
+	c = smc_dismount (rqst_context->rpfd, extended_robot_info.smc_fd,
+	  extended_robot_info.smc_ldr, &extended_robot_info.robot_info, drvord,
+	  force == 0 ? vid : "");
 	if (c) c += ERMCRBTERR;
-	RETURN (c);
+	rmc_logit (func, "returns %d\n", c);
+	return c;
+}
+
+int rmc_srv_genericmount(struct rmc_srv_rqst_context *const rqst_context) {
+	return 0;
+}
+
+int rmc_srv_genericunmount(struct rmc_srv_rqst_context *const rqst_context) {
+	return 0;
 }
diff --git a/rmc/rmc_read_elem_status.c b/rmc/rmc_read_elem_status.c
index b8c92ef609e05ac985ecd312417b91d60b0f9966..551c256b827ba9b1edf7457ce1da495ae05bf0eb 100644
--- a/rmc/rmc_read_elem_status.c
+++ b/rmc/rmc_read_elem_status.c
@@ -9,10 +9,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <netinet/in.h>
-#include "marshall.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/marshall.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 int rmc_read_elem_status(
 	const char *const server,
 	const int type,
@@ -27,9 +27,9 @@ int rmc_read_elem_status(
 	int msglen;
 	char *q;
 	char *rbp;
-	char repbuf[REPBUFSZ];
+	char repbuf[RMC_REPBUFSZ];
 	char *sbp;
-	char sendbuf[REQBUFSZ];
+	char sendbuf[RMC_REQBUFSZ];
 	uid_t uid;
 
 	uid = getuid();
@@ -58,7 +58,7 @@ int rmc_read_elem_status(
 
         while ((c = send2rmc (server, sendbuf, msglen, repbuf, sizeof(repbuf))) &&
             serrno == ERMCNACT)
-                sleep (RETRYI);
+                sleep (RMC_RETRYI);
 	if (c == 0) {
 		rbp = repbuf;
 		unmarshall_LONG (rbp, c);
diff --git a/rmc/rmc_send_scsi_cmd.c b/rmc/rmc_send_scsi_cmd.c
new file mode 100644
index 0000000000000000000000000000000000000000..3fd4163043da0b127fd99d227318b4f225eccbb3
--- /dev/null
+++ b/rmc/rmc_send_scsi_cmd.c
@@ -0,0 +1,299 @@
+/*
+ * 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 <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 "h/scsictl.h"
+#include "h/serrno.h"
+#include "h/rmc_send_scsi_cmd.h"
+static char rmc_err_msgbuf[132];
+static 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 timeout,   /* in milliseconds */
+	const int flags,
+	int *const nb_sense_ret,
+	char **const msgaddr)
+{
+	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 = 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/rmc/sendrep.c b/rmc/rmc_sendrep.c
similarity index 78%
rename from rmc/sendrep.c
rename to rmc/rmc_sendrep.c
index 1cf84fa5b96cfc13747c0dbb64fe4c650a707b0d..eefee63be6fbeaa655565ed985c1dcd17fa6eee3 100644
--- a/rmc/sendrep.c
+++ b/rmc/rmc_sendrep.c
@@ -9,25 +9,28 @@
 #include <string.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include "marshall.h"
-#include "net.h"
-#include "rmc.h"
-#include "tplogger_api.h"
+#include "h/marshall.h"
+#include "h/net.h"
+#include "h/rmc_constants.h"
+#include "h/rmc_logit.h"
+#include "h/rmc_sendrep.h"
+#include "h/tplogger_api.h"
 #include <unistd.h>
 
-int sendrep(int rpfd, int rep_type, ...)
+int rmc_sendrep(const int rpfd, const int rep_type, ...)
 {
 	va_list args;
 	char func[16];
 	char *msg;
 	int n;
-	char prtbuf[PRTBUFSZ];
+	char prtbuf[RMC_PRTBUFSZ];
 	char *rbp;
 	int rc;
-	char repbuf[REPBUFSZ];
+	char repbuf[RMC_REPBUFSZ];
 	int repsize;
 
-	strncpy (func, "sendrep", 16);
+	strncpy (func, "rmc_sendrep", sizeof(func));
+	func[sizeof(func) - 1] = '\0';
 	rbp = repbuf;
 	marshall_LONG (rbp, RMC_MAGIC);
 	va_start (args, rep_type);
@@ -38,7 +41,7 @@ int sendrep(int rpfd, int rep_type, ...)
 		vsprintf (prtbuf, msg, args);
 		marshall_LONG (rbp, strlen (prtbuf) + 1);
 		marshall_STRING (rbp, prtbuf);
-		rmclogit (func, "%s", prtbuf);
+		rmc_logit (func, "%s", prtbuf);
                 tl_rmcdaemon.tl_log( &tl_rmcdaemon, 103, 2,
                                      "func"   , TL_MSG_PARAM_STR, func,
                                      "Message", TL_MSG_PARAM_STR, prtbuf );
@@ -58,7 +61,7 @@ int sendrep(int rpfd, int rep_type, ...)
 	va_end (args);
 	repsize = rbp - repbuf;
 	if (netwrite (rpfd, repbuf, repsize) != repsize) {
-		rmclogit (func, RMC02, "send", neterror());
+		rmc_logit (func, RMC02, "send", neterror());
                 tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 3,
                                      "func" , TL_MSG_PARAM_STR, func,
                                      "On"   , TL_MSG_PARAM_STR, "send",
diff --git a/rmc/rmc_serv.c b/rmc/rmc_serv.c
index fef906b2bf86d9e4e1715eae56a7f41da6700506..d2cff293f36ea496490cf038509bc44af59be0b1 100644
--- a/rmc/rmc_serv.c
+++ b/rmc/rmc_serv.c
@@ -17,37 +17,39 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include "Cinit.h"
-#include "marshall.h"
-#include "net.h"
-#include "rmc.h"
-#include "scsictl.h"
-#include "serrno.h"
-#include "rmc_server_api.h"
-#include "Cdomainname.h"
-#include "tplogger_api.h"
+#include "h/Cinit.h"
+#include "h/marshall.h"
+#include "h/net.h"
+#include "h/rmc_constants.h"
+#include "h/rmc_logit.h"
+#include "h/rmc_procreq.h"
+#include "h/rmc_sendrep.h"
+#include "h/rmc_smcsubr.h"
+#include "h/scsictl.h"
+#include "h/serrno.h"
+#include "h/Cdomainname.h"
+#include "h/tplogger_api.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <Ctape_api.h>
-#include "sendscsicmd.h"
+#include "h/sendscsicmd.h"
 
 /* Forward declaration */
-int getreq(int, int*, char*, char**);
-void procreq(int, char*, char*);
+static int getreq(const int s, int *const req_type, char *const req_data,
+  char **const clienthost);
+static void procreq(const int rpfd, const int req_type, char *const req_data,
+  char *const clienthost);
+static void rmc_doit(const int rpfd);
 
-int being_shutdown;
-char func[16];
 int jid;
 char localhost[CA_MAXHOSTNAMELEN+1];
 int maxfds;
 struct extended_robot_info extended_robot_info;
-int rpfd;
 
 int rmc_main(struct main_args *main_args)
 {
 	int c;
 	unsigned char cdb[12];
-	void doit(int);
 	char domainname[CA_MAXHOSTNAMELEN+1];
 	struct sockaddr_in from;
 	socklen_t fromlen = sizeof(from);
@@ -55,11 +57,9 @@ int rmc_main(struct main_args *main_args)
 	char *msgaddr;
 	int nb_sense_ret;
 	int on = 1;	/* for REUSEADDR */
-	char *p;
 	char plist[40];
 	fd_set readfd, readmask;
 	char *robot;
-	int rqfd;
 	int s;
     int n=0;
 	char sense[MAXSENSE];
@@ -67,22 +67,14 @@ int rmc_main(struct main_args *main_args)
 	struct smc_status smc_status;
 	struct servent *sp;
 	struct timeval timeval;
+	char func[16];
+
+	strncpy (func, "rmc_serv", sizeof(func));
+	func[sizeof(func) - 1] = '\0';
 
         /* init the tplogger interface */
         {
-                mode_t save_mask;
-                /* char *p; */
-                
-                save_mask = umask(0);
-
-                /*
-                p = getconfent ("TAPE", "TPLOGGER", 0);
-                if (p && (0 == strcasecmp(p, "SYSLOG"))) {
-                        tl_init_handle( &tl_rtcpd, "syslog" ); 
-                } else {
-                        tl_init_handle( &tl_rmc, "dlf" );  
-                }
-                */
+                const mode_t save_mask = umask(0);
 
                 /* only syslog support */
                 tl_init_handle( &tl_rmcdaemon, "syslog" );
@@ -95,8 +87,7 @@ int rmc_main(struct main_args *main_args)
         }
 
 	jid = getpid();
-	strncpy (func, "rmc_serv", 16);
-	rmclogit (func, "started\n");
+	rmc_logit (func, "started\n");
         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 109, 2,
                              "func"   , TL_MSG_PARAM_STR, "rmc_main",
                              "Message", TL_MSG_PARAM_STR, "Daemon started" );        
@@ -104,7 +95,7 @@ int rmc_main(struct main_args *main_args)
 	gethostname (localhost, CA_MAXHOSTNAMELEN+1);
 	if (strchr (localhost, '.') == NULL) {
 		if (Cdomainname (domainname, sizeof(domainname)) < 0) {
-			rmclogit (func, "Unable to get domainname\n");
+			rmc_logit (func, "Unable to get domainname\n");
                         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 103, 2,
                                              "func"   , TL_MSG_PARAM_STR, "rmc_main",
                                              "Message", TL_MSG_PARAM_STR, "Unable to get domainname" );
@@ -114,7 +105,7 @@ int rmc_main(struct main_args *main_args)
 	}
 
 	if (main_args->argc != 2) {
-		rmclogit (func, RMC01);
+		rmc_logit (func, RMC01);
                 tl_rmcdaemon.tl_log( &tl_rmcdaemon, 1, 1,
                                      "func", TL_MSG_PARAM_STR, "rmc_main" );
 		exit (USERR);
@@ -122,7 +113,7 @@ int rmc_main(struct main_args *main_args)
 	robot = main_args->argv[1];
 	if (*robot == '\0' ||
 	    (strlen (robot) + (*robot == '/') ? 0 : 5) > CA_MAXRBTNAMELEN) {
-		rmclogit (func, RMC06, "robot");
+		rmc_logit (func, RMC06, "robot");
                 tl_rmcdaemon.tl_log( &tl_rmcdaemon, 6, 2,
                                      "func", TL_MSG_PARAM_STR, "rmc_main",
                                      "For" , TL_MSG_PARAM_STR, "robot" );
@@ -142,20 +133,20 @@ int rmc_main(struct main_args *main_args)
                                  extended_robot_info.smc_ldr,
                                  &extended_robot_info.robot_info))) {
             c = smc_lasterror (&smc_status, &msgaddr);
-            rmclogit (func, RMC02, "get_geometry", msgaddr);
+            rmc_logit (func, RMC02, "get_geometry", msgaddr);
             tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 4,
                                  "func"    , TL_MSG_PARAM_STR, "rmc_main",
                                  "On"      , TL_MSG_PARAM_STR, "get_geometry",
                                  "Message" , TL_MSG_PARAM_STR, msgaddr,
                                  "NextStep", TL_MSG_PARAM_STR, "Retry" );
 
-            rmclogit (func,"trying again get_geometry\n");
+            rmc_logit (func,"trying again get_geometry\n");
             tl_rmcdaemon.tl_log( &tl_rmcdaemon, 110, 2,
                                  "func"   , TL_MSG_PARAM_STR, "rmc_main",
                                  "Message", TL_MSG_PARAM_STR, "trying again get_geometry" );
             n++;
             if (n==2) {
-              rmclogit (func, RMC02, "get_geometry", msgaddr);
+              rmc_logit (func, RMC02, "get_geometry", msgaddr);
               tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 4,
                                    "func"    , TL_MSG_PARAM_STR, "rmc_main",
                                    "On"      , TL_MSG_PARAM_STR, "get_geometry",
@@ -185,7 +176,7 @@ int rmc_main(struct main_args *main_args)
 		    sense[12] == 0x20) {
 			extended_robot_info.smc_support_voltag = 0;
 		} else {
-			rmclogit (func, RMC02, "find_cartridge", msgaddr);
+			rmc_logit (func, RMC02, "find_cartridge", msgaddr);
                         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 4,
                                              "func"    , TL_MSG_PARAM_STR, "rmc_main",
                                              "On"      , TL_MSG_PARAM_STR, "find_cartridge",
@@ -202,7 +193,7 @@ int rmc_main(struct main_args *main_args)
 	/* open request socket */
 
 	if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
-		rmclogit (func, RMC02, "socket", neterror());
+		rmc_logit (func, RMC02, "socket", neterror());
                 tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 4,
                                      "func"    , TL_MSG_PARAM_STR, "rmc_main",
                                      "On"      , TL_MSG_PARAM_STR, "socket",
@@ -212,23 +203,26 @@ int rmc_main(struct main_args *main_args)
 	}
 	memset ((char *)&sin, 0, sizeof(struct sockaddr_in)) ;
 	sin.sin_family = AF_INET ;
-	if ((p = getenv ("RMC_PORT")) || (p = getconfent ("RMC", "PORT", 0))) {
-		sin.sin_port = htons ((unsigned short)atoi (p));
-	} else if ((sp = getservbyname ("rmc", "tcp"))) {
-		sin.sin_port = sp->s_port;
-	} else {
-		sin.sin_port = htons ((unsigned short)RMC_PORT);
+	{
+		const char *p;
+		if ((p = getenv ("RMC_PORT")) || (p = getconfent ("RMC", "PORT", 0))) {
+			sin.sin_port = htons ((unsigned short)atoi (p));
+		} else if ((sp = getservbyname ("rmc", "tcp"))) {
+			sin.sin_port = sp->s_port;
+		} else {
+			sin.sin_port = htons ((unsigned short)RMC_PORT);
+		}
 	}
 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
 	if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) {
-		rmclogit (func, RMC02, "setsockopt", neterror());
+		rmc_logit (func, RMC02, "setsockopt", neterror());
                 tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 3,
                                      "func"    , TL_MSG_PARAM_STR, "rmc_main",
                                      "On"      , TL_MSG_PARAM_STR, "setsockopt",
                                      "Message" , TL_MSG_PARAM_STR, msgaddr );
         }
 	if (bind (s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
-		rmclogit (func, RMC02, "bind", neterror());
+		rmc_logit (func, RMC02, "bind", neterror());
                 tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 4,
                                      "func"    , TL_MSG_PARAM_STR, "rmc_main",
                                      "On"      , TL_MSG_PARAM_STR, "bind",
@@ -245,12 +239,12 @@ int rmc_main(struct main_args *main_args)
 	while (1) {
 		if (FD_ISSET (s, &readfd)) {
 			FD_CLR (s, &readfd);
-			rqfd = accept (s, (struct sockaddr *) &from, &fromlen);
-			rpfd = rqfd;
-			(void) doit (rqfd);
+			const int rpfd =
+				accept (s, (struct sockaddr *) &from, &fromlen);
+			(void) rmc_doit (rpfd);
 		}
 		memcpy (&readfd, &readmask, sizeof(readmask));
-		timeval.tv_sec = CHECKI;
+		timeval.tv_sec = RMC_CHECKI;
 		timeval.tv_usec = 0;
 		if (select (maxfds, &readfd, (fd_set *)0, (fd_set *)0, &timeval) < 0) {
 			FD_ZERO (&readfd);
@@ -272,25 +266,26 @@ int main(int argc,
 	exit (rmc_main (&main_args));
 }
 
-void doit(int rqfd)
+static void rmc_doit(const int rpfd)
 {
 	int c;
 	char *clienthost;
-	char req_data[REQBUFSZ-3*LONGSIZE];
+	char req_data[RMC_REQBUFSZ-3*LONGSIZE];
 	int req_type = 0;
 
-	if ((c = getreq (rqfd, &req_type, req_data, &clienthost)) == 0)
-		procreq (req_type, req_data, clienthost);
+	if ((c = getreq (rpfd, &req_type, req_data, &clienthost)) == 0)
+		procreq (rpfd, req_type, req_data, clienthost);
 	else if (c > 0)
-		sendrep (rqfd, RMC_RC, c);
+		rmc_sendrep (rpfd, RMC_RC, c);
 	else
-		close (rqfd);
+		close (rpfd);
 }
 
-int getreq(int s,
-           int *req_type,
-           char *req_data,
-           char **clienthost)
+static int getreq(
+  const int s,
+  int *const req_type,
+  char *const req_data,
+  char **const clienthost)
 {
 	struct sockaddr_in from;
 	socklen_t fromlen = sizeof(from);
@@ -301,6 +296,10 @@ int getreq(int s,
 	int n;
 	char *rbp;
 	char req_hdr[3*LONGSIZE];
+	char func[16];
+
+	strncpy (func, "rmc_serv", sizeof(func));
+	func[sizeof(func) - 1] = '\0';
 
 	l = netread_timeout (s, req_hdr, sizeof(req_hdr), RMC_TIMEOUT);
 	if (l == sizeof(req_hdr)) {
@@ -309,20 +308,17 @@ int getreq(int s,
 		unmarshall_LONG (rbp, n);
 		*req_type = n;
 		unmarshall_LONG (rbp, msglen);
-		if (msglen > REQBUFSZ) {
-			rmclogit (func, RMC46, REQBUFSZ);
+		if (msglen > RMC_REQBUFSZ) {
+			rmc_logit (func, RMC46, RMC_REQBUFSZ);
                         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 46, 2,
                                              "func"   , TL_MSG_PARAM_STR, "getreq",
-                                             "MaxSize", TL_MSG_PARAM_INT,  REQBUFSZ);                        
+                                             "MaxSize", TL_MSG_PARAM_INT,  RMC_REQBUFSZ);                        
 			return (-1);
 		}
 		l = msglen - sizeof(req_hdr);
 		n = netread_timeout (s, req_data, l, RMC_TIMEOUT);
-		if (being_shutdown) {
-			return (ERMCNACT);
-		}
 		if (getpeername (s, (struct sockaddr *) &from, &fromlen) < 0) {
-			rmclogit (func, RMC02, "getpeername", neterror());
+			rmc_logit (func, RMC02, "getpeername", neterror());
                         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 4,
                                              "func"   , TL_MSG_PARAM_STR, "getreq",
                                              "On"     , TL_MSG_PARAM_STR, "getpeername",
@@ -339,13 +335,13 @@ int getreq(int s,
 		return (0);
 	} else {
 		if (l > 0) {
-			rmclogit (func, RMC04, l);
+			rmc_logit (func, RMC04, l);
                         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 4, 3,
                                              "func"   , TL_MSG_PARAM_STR, "getreq",
                                              "netread", TL_MSG_PARAM_INT, l,
                                              "Return" , TL_MSG_PARAM_STR, "ERMCUNREC" );
 		} else if (l < 0) {
-			rmclogit (func, RMC02, "netread", strerror(errno));
+			rmc_logit (func, RMC02, "netread", strerror(errno));
                         tl_rmcdaemon.tl_log( &tl_rmcdaemon, 2, 4,
                                              "func"   , TL_MSG_PARAM_STR, "getreq",
                                              "On"     , TL_MSG_PARAM_STR, "netread",
@@ -356,37 +352,53 @@ int getreq(int s,
 	}
 }
 
-void procreq(int req_type,
-             char *req_data,
-             char *clienthost)
+static void procreq(
+  const int rpfd,
+  const int req_type,
+  char *const req_data,
+  char *const clienthost)
 {
-	int c;
+	int c = 0;
+	struct rmc_srv_rqst_context rqst_context;
+
+	rqst_context.localhost = localhost;
+	rqst_context.rpfd = rpfd;
+	rqst_context.req_data = req_data;
+	rqst_context.clienthost = clienthost;
 
 	switch (req_type) {
 	case RMC_MOUNT:
-		c = rmc_srv_mount (req_data, clienthost);
+		c = rmc_srv_mount (&rqst_context);
 		break;
 	case RMC_UNMOUNT:
-		c = rmc_srv_unmount (req_data, clienthost);
+		c = rmc_srv_unmount (&rqst_context);
 		break;
 	case RMC_EXPORT:
-		c = rmc_srv_export (req_data, clienthost);
+		c = rmc_srv_export (&rqst_context);
 		break;
 	case RMC_IMPORT:
-		c = rmc_srv_import (req_data, clienthost);
+		c = rmc_srv_import (&rqst_context);
 		break;
 	case RMC_GETGEOM:
-		c = rmc_srv_getgeom (req_data, clienthost);
+		c = rmc_srv_getgeom (&rqst_context);
 		break;
 	case RMC_READELEM:
-		c = rmc_srv_readelem (req_data, clienthost);
+		c = rmc_srv_readelem (&rqst_context);
 		break;
 	case RMC_FINDCART:
-		c = rmc_srv_findcart (req_data, clienthost);
+		c = rmc_srv_findcart (&rqst_context);
+		break;
+/*
+	case RMC_GENERICMOUNT:
+		c = rmc_srv_genericmount (localhost, rpfd, req_data, clienthost);
+		break;
+	case RMC_GENERICUNMOUNT:
+		c = rmc_srv_genericunmount (localhost, rpfd, req_data, clienthost);
 		break;
+*/
 	default:
-		sendrep (rpfd, MSG_ERR, RMC03, req_type);
+		rmc_sendrep (rpfd, MSG_ERR, RMC03, req_type);
 		c = ERMCUNREC;
 	}
-	sendrep (rpfd, RMC_RC, c);
+	rmc_sendrep (rpfd, RMC_RC, c);
 }
diff --git a/rmc/smcsubr.c b/rmc/rmc_smcsubr.c
similarity index 56%
rename from rmc/smcsubr.c
rename to rmc/rmc_smcsubr.c
index bf4f0e742302d44023897ccd4792e4a67309928c..d1954d2b6eeb1efdd1afc3b7e1000b817354e994 100644
--- a/rmc/smcsubr.c
+++ b/rmc/rmc_smcsubr.c
@@ -5,28 +5,33 @@
  
 
 #include <errno.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
-#include "Ctape.h"
-#include "scsictl.h"
-#include "serrno.h"
-#include "smc.h"
-#include "sendscsicmd.h"
-#include "getconfent.h"
+
+#include "h/Ctape.h"
+#include "h/rmc_constants.h"
+#include "h/rmc_sendrep.h"
+#include "h/rmc_smcsubr.h"
+#include "h/rmc_smcsubr2.h"
+#include "h/scsictl.h"
+#include "h/serrno.h"
+#include "h/sendscsicmd.h"
+#include "h/smc_constants.h"
+#include "h/getconfent.h"
 
 #define	RBT_XTRA_PROC 10
 static struct smc_status smc_status;
 static char *smc_msgaddr;
 
-static void
-save_error(rc, nb_sense, sense, msgaddr)
-int rc;
-int nb_sense;
-char *sense;
-char *msgaddr;
+static void save_error(
+	const int rc,
+	const int nb_sense,
+	const char *const sense,
+	char *const msgaddr)
 {
 	smc_msgaddr = msgaddr;
 	smc_status.rc = rc;
@@ -44,11 +49,10 @@ char *msgaddr;
 	}
 }
 
-static int
-vmatch (char *pattern, char *vid)
+static int vmatch (const char *const pattern, const char *const vid)
 {
-	char *p;
-	char *v;
+	const char *p;
+	const char *v;
 
 	for (p = pattern, v = vid; *p; p++, v++) {
 		if (*v == 0 && *p != '*')
@@ -72,10 +76,10 @@ vmatch (char *pattern, char *vid)
 	return (*v != 0);
 }
 
-static int
-get_element_size(int fd,
-                            char *rbtdev,
-                            int type)
+static int get_element_size(
+	const int fd,
+	const char *const rbtdev,
+	const int type)
 {
 	unsigned char buf[128];
 	unsigned char cdb[12];
@@ -117,14 +121,14 @@ get_element_size(int fd,
 	return (buf[10] * 256 + buf[11]);
 }
 
-static int
-get_element_info(char opcode,
-                            int fd,
-                            char *rbtdev,
-                            int type,
-                            int start,
-                            int nbelem,
-                            struct smc_element_info element_info[])
+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];
@@ -237,9 +241,10 @@ get_element_info(char opcode,
 	return (avail_elem);
 }
 
-int smc_get_geometry(int fd,
-                     char *rbtdev,
-                     struct robot_info *robot_info)
+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];
@@ -321,12 +326,13 @@ int smc_get_geometry(int fd,
 	return (0);
 }
 
-int smc_read_elem_status(int fd,
-			 char *rbtdev,
-			 int type,
-			 int start,
-			 int nbelem,
-			 struct smc_element_info element_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[])
 {
 	char func[16];
 	int rc;
@@ -338,13 +344,14 @@ int smc_read_elem_status(int fd,
 	return (rc);
 }
 
-int smc_find_cartridge2 (int fd,
-                         char *rbtdev,
-                         char *template,
-                         int type,
-                         int start,
-                         int nbelem,
-                         struct smc_element_info element_info[])
+int smc_find_cartridge2 (
+	const int fd,
+	const char *const rbtdev,
+	const char *const template,
+	const int type,
+	const int start,
+	const int nbelem,
+	struct smc_element_info element_info[])
 {
 	int c;
 	static char err_msgbuf[132];
@@ -404,13 +411,14 @@ int smc_find_cartridge2 (int fd,
 }
 
 
-int smc_find_cartridge(int fd,
-                       char *rbtdev,
-                       char *template,
-                       int type,
-                       int start,
-                       int nbelem,
-                       struct smc_element_info element_info[])
+int smc_find_cartridge(
+	const int fd,
+	const char *const rbtdev,
+	const char *const template,
+	const int type,
+	const int start,
+	const int nbelem,
+	struct smc_element_info element_info[])
 {
 	unsigned char cdb[12];
 	char func[16];
@@ -488,7 +496,7 @@ struct scsierr_codact {
 	short action;
 	char *txt;
 };
-struct scsierr_codact scsierr_acttbl[] = {
+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"},
@@ -528,8 +536,9 @@ struct scsierr_codact scsierr_acttbl[] = {
     {0x02, 0x5A, 0x01, RBT_NORETRY, "Operator Medium Removal Request"}
 };
 
-int smc_lasterror(struct smc_status *smc_stat,
-                  char **msgaddr)
+int smc_lasterror(
+	struct smc_status *const smc_stat,
+	char **const msgaddr)
 {
 	unsigned int i;
 
@@ -555,11 +564,12 @@ int smc_lasterror(struct smc_status *smc_stat,
 	return (RBT_NORETRY);
 }
 
-int smc_move_medium(int fd,
-                    char *rbtdev,
-                    int from,
-                    int to,
-                    int invert)
+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];
@@ -603,3 +613,341 @@ int smc_move_medium(int fd,
 	}
 	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_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 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];
+	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)) {
+              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);
+}
+
+int smc_export (
+	const int rpfd,
+	const int fd,
+	const char *const loader,
+	struct robot_info *const robot_info,
+	const char *const vid)
+{
+        int c;
+        struct smc_element_info element_info;
+	char func[16];
+	int i;
+        struct smc_element_info *impexp_info;
+	char *msgaddr;
+	int nbelem;
+	struct smc_status smc_status;
+ 
+	strncpy (func, "smc_export", sizeof(func));
+	func[sizeof(func) - 1] = '\0';
+
+	if ((c = smc_find_cartridge (fd, loader, vid, 0, 0, 1, &element_info)) < 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, SR017, "export", vid, "volume not in library");
+		return (RBT_NORETRY);
+	}
+	if (element_info.element_type != 2) {
+		rmc_usrmsg ( rpfd, func, SR017, "export", vid, "volume in use");
+		return (RBT_SLOW_RETRY);
+	}
+	/* look for a free export slot */
+
+	nbelem = robot_info->port_count;
+	if ((impexp_info = malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
+		rmc_usrmsg ( rpfd, func, SR012);
+		return (RBT_NORETRY);
+	}
+
+	if ((c = smc_read_elem_status (fd, loader, 3, robot_info->port_start,
+	    nbelem, impexp_info)) < 0) {
+		c = smc_lasterror (&smc_status, &msgaddr);
+		rmc_usrmsg ( rpfd, func, SR020, "read_elem_status", msgaddr);
+		free (impexp_info);
+		return (c);
+	}
+	for (i = 0; i < nbelem; i++) {
+		if (((impexp_info+i)->state & 0x1) == 0)	/* element free */
+			break;
+	}
+	if (i >= nbelem) {	/* export slots are full */
+		rmc_usrmsg ( rpfd, func, SR013);
+		free (impexp_info);
+		return (RBT_NORETRY);
+	}
+
+	if ((c = smc_move_medium (fd, loader, element_info.element_address,
+	    (impexp_info+i)->element_address, 0)) < 0) {
+		c = smc_lasterror (&smc_status, &msgaddr);
+		rmc_usrmsg ( rpfd, func, SR017, "export", vid, msgaddr);
+		free (impexp_info);
+		return (c);
+	}
+	free (impexp_info);
+	return (0);
+}
+
+int smc_import (
+	const int rpfd,
+	const int fd,
+	const char *const loader,
+	struct robot_info *const robot_info,
+	const char *const vid)
+{
+        int c;
+	int device_start;
+        struct smc_element_info *element_info;
+	char func[16];
+	int i, j;
+	char *msgaddr;
+	int nbelem;
+	int port_start;
+	int slot_start;
+	struct smc_status smc_status;
+ 
+	strncpy (func, "smc_import", sizeof(func));
+	func[sizeof(func) - 1] = '\0';
+
+	nbelem = robot_info->transport_count + robot_info->slot_count +
+		 robot_info->port_count + robot_info->device_count;
+	if ((element_info = malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
+		rmc_usrmsg ( rpfd, func, SR012);
+		return (RBT_NORETRY);
+	}
+
+	/* get inventory */
+
+	if ((c = smc_read_elem_status (fd, loader, 0, 0, nbelem, element_info)) < 0) {
+		c = smc_lasterror (&smc_status, &msgaddr);
+		rmc_usrmsg ( rpfd, func, SR020, "read_elem_status", msgaddr);
+		free (element_info);
+		return (c);
+	}
+	for (i = 0; i < c; i++)
+		if ((element_info+i)->element_type == 2) break;
+	slot_start = i;
+	for (i = 0; i < c; i++)
+		if ((element_info+i)->element_type == 3) break;
+	port_start = i;
+	for (i = 0; i < c; i++)
+		if ((element_info+i)->element_type == 4) break;
+	device_start = i;
+
+	/* mark home slots of cartridges currently on drives as non free */
+
+	for (i = device_start; i < device_start+robot_info->device_count; i++) {
+		if (((element_info+i)->state & 0x1) == 0) continue;
+		for (j = slot_start; j < slot_start+robot_info->slot_count; j++)
+			if ((element_info+i)->source_address ==
+				(element_info+j)->element_address) break;
+		(element_info+j)->state |= 1;
+	}
+
+	/* loop on all import slots */
+
+	for (i = port_start; i < port_start+robot_info->port_count; i++) {
+		if (*vid && strcmp (vid, (element_info+i)->name)) continue;
+		if (*vid || (*vid == '\0' && ((element_info+i)->state & 2))) {
+
+			/* find a free storage slot */
+
+			for (j = slot_start; j < slot_start+robot_info->slot_count; j++)
+				if (((element_info+j)->state & 0x1) == 0) break;
+			if (j >= slot_start+robot_info->slot_count) {
+				rmc_usrmsg ( rpfd, func, SR015);
+				free (element_info);
+				return (RBT_NORETRY);
+			}
+
+			if ((c = smc_move_medium (fd, loader, (element_info+i)->element_address,
+			    (element_info+j)->element_address, 0)) < 0) {
+				c = smc_lasterror (&smc_status, &msgaddr);
+				rmc_usrmsg ( rpfd, func, SR017, "import",
+				    (element_info+i)->name, msgaddr);
+				free (element_info);
+				return (c);
+			}
+			if (*vid || c) break;
+			(element_info+j)->state |= 1;	/* dest slot is now full */
+		}
+	}
+	free (element_info);
+	return (c);
+}
+
+int smc_mount (
+	const int rpfd,
+	const int fd,
+	const char *const loader,
+	struct robot_info *const robot_info,
+	const int drvord,
+	const char *const vid,
+	const int invert)
+{
+    int c;
+    struct smc_element_info element_info;
+	char func[16];
+	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)) < 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);
+}
diff --git a/rmc/send2rmc.c b/rmc/send2rmc.c
index 69b265204eacaddae841a01a36aaea37c088371c..c2464e25810771ba73ddd79bdbc4dbc3a39a693c 100644
--- a/rmc/send2rmc.c
+++ b/rmc/send2rmc.c
@@ -10,12 +10,12 @@
 #include <unistd.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include "Cnetdb.h"
-#include "marshall.h"
-#include "net.h"
-#include "rmc.h"
-#include "rmc_api.h"
-#include "serrno.h"
+#include "h/Cnetdb.h"
+#include "h/marshall.h"
+#include "h/net.h"
+#include "h/rmc_api.h"
+#include "h/rmc_constants.h"
+#include "h/serrno.h"
 
 /* send2tpd - send a request to the SCSI media changer server and wait for the reply */
 
@@ -35,9 +35,9 @@ int send2rmc(
 	int magic;
 	int n;
 	char *p;
-	char prtbuf[PRTBUFSZ];
+	char prtbuf[RMC_PRTBUFSZ];
 	int rep_type;
-	char repbuf[REPBUFSZ];
+	char repbuf[RMC_REPBUFSZ];
 	char rmchost[CA_MAXHOSTNAMELEN+1];
 	int s;
 	struct sockaddr_in sin; /* internet socket */
diff --git a/rmc/smc.c b/rmc/smc.c
index 1eb6b843957f9e5f2fcee41154e496678f37d123..7d5b1cf4ba9edd1671678d7688165efd102c184d 100644
--- a/rmc/smc.c
+++ b/rmc/smc.c
@@ -9,11 +9,11 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include "Ctape.h"
-#include "rmc_api.h"
-#include "serrno.h"
-#include "smc.h"
-#include "getconfent.h"
+#include "h/Ctape.h"
+#include "h/rmc_api.h"
+#include "h/serrno.h"
+#include "h/smc_constants.h"
+#include "h/getconfent.h"
 			/* exit codes */
 
 #define	USERR	1
diff --git a/rmc/smcsubr2.c b/rmc/smcsubr2.c
deleted file mode 100644
index 95d47c0b4ecefa66c4ed12ead7bce53b3a78e248..0000000000000000000000000000000000000000
--- a/rmc/smcsubr2.c
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 1998-2003 by CERN/IT/PDP/DM
- * All rights reserved
- */
- 
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include "Ctape.h"
-#include "Ctape_api.h"
-#include "getconfent.h"
-#include "serrno.h"
-#include "smc.h"
-
-/* from smcsubr.c */
-extern int smc_read_elem_status(int, char *, int, int, int, struct smc_element_info[]);
-extern int smc_lasterror(struct smc_status *, char **);
-extern int smc_move_medium(int, char *, int, int, int);
-extern int smc_find_cartridge(int, char *, char *, int, int, int, struct smc_element_info[] );
-
-int smc_dismount (int fd,
-                  char *loader,
-                  struct robot_info *robot_info,
-                  int drvord,
-                  char *vid)
-{
-	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];
-	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);
-			usrmsg (func, SR020, "read_elem_status", msgaddr);
-			return (smc_error);
-		}
-		if (0 == (drive_element_info.state & 0x1)) {
-			usrmsg (func, SR018, "demount", vid, drvord, "Medium Not Present");
-			return (RBT_OK);
-		}
-
-		drive_not_unloaded = (0 == (drive_element_info.state & 0x8));
-		if (drive_not_unloaded) {
-			usrmsg (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) {
-		usrmsg (func, SR018, "demount", vid, drvord, "Drive Not Unloaded");
-		return (RBT_UNLD_DMNT);
-	}
-
-	if (*vid && strcmp (drive_element_info.name, vid)) {
-		usrmsg (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);
-		usrmsg (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)) {
-              const int smc_error = smc_lasterror (&smc_status, &msgaddr);
-              usrmsg (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);
-}
-
-int smc_export (int fd,
-                char *loader,
-                struct robot_info *robot_info,
-                char *vid)
-{
-        int c;
-        struct smc_element_info element_info;
-	char func[16];
-	int i;
-        struct smc_element_info *impexp_info;
-	char *msgaddr;
-	int nbelem;
-	struct smc_status smc_status;
- 
-	strncpy (func, "smc_export", sizeof(func));
-	func[sizeof(func) - 1] = '\0';
-
-	if ((c = smc_find_cartridge (fd, loader, vid, 0, 0, 1, &element_info)) < 0) {
-		c = smc_lasterror (&smc_status, &msgaddr);
-		usrmsg (func, SR017, "find_cartridge", vid, msgaddr);
-		return (c);
-	}
-	if (c == 0) {
-		usrmsg (func, SR017, "export", vid, "volume not in library");
-		return (RBT_NORETRY);
-	}
-	if (element_info.element_type != 2) {
-		usrmsg (func, SR017, "export", vid, "volume in use");
-		return (RBT_SLOW_RETRY);
-	}
-	/* look for a free export slot */
-
-	nbelem = robot_info->port_count;
-	if ((impexp_info = malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
-		usrmsg (func, SR012);
-		return (RBT_NORETRY);
-	}
-
-	if ((c = smc_read_elem_status (fd, loader, 3, robot_info->port_start,
-	    nbelem, impexp_info)) < 0) {
-		c = smc_lasterror (&smc_status, &msgaddr);
-		usrmsg (func, SR020, "read_elem_status", msgaddr);
-		free (impexp_info);
-		return (c);
-	}
-	for (i = 0; i < nbelem; i++) {
-		if (((impexp_info+i)->state & 0x1) == 0)	/* element free */
-			break;
-	}
-	if (i >= nbelem) {	/* export slots are full */
-		usrmsg (func, SR013);
-		free (impexp_info);
-		return (RBT_NORETRY);
-	}
-
-	if ((c = smc_move_medium (fd, loader, element_info.element_address,
-	    (impexp_info+i)->element_address, 0)) < 0) {
-		c = smc_lasterror (&smc_status, &msgaddr);
-		usrmsg (func, SR017, "export", vid, msgaddr);
-		free (impexp_info);
-		return (c);
-	}
-	free (impexp_info);
-	return (0);
-}
-
-int smc_import (int fd,
-                char *loader,
-                struct robot_info *robot_info,
-                char *vid)
-{
-        int c;
-	int device_start;
-        struct smc_element_info *element_info;
-	char func[16];
-	int i, j;
-	char *msgaddr;
-	int nbelem;
-	int port_start;
-	int slot_start;
-	struct smc_status smc_status;
- 
-	strncpy (func, "smc_import", sizeof(func));
-	func[sizeof(func) - 1] = '\0';
-
-	nbelem = robot_info->transport_count + robot_info->slot_count +
-		 robot_info->port_count + robot_info->device_count;
-	if ((element_info = malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
-		usrmsg (func, SR012);
-		return (RBT_NORETRY);
-	}
-
-	/* get inventory */
-
-	if ((c = smc_read_elem_status (fd, loader, 0, 0, nbelem, element_info)) < 0) {
-		c = smc_lasterror (&smc_status, &msgaddr);
-		usrmsg (func, SR020, "read_elem_status", msgaddr);
-		free (element_info);
-		return (c);
-	}
-	for (i = 0; i < c; i++)
-		if ((element_info+i)->element_type == 2) break;
-	slot_start = i;
-	for (i = 0; i < c; i++)
-		if ((element_info+i)->element_type == 3) break;
-	port_start = i;
-	for (i = 0; i < c; i++)
-		if ((element_info+i)->element_type == 4) break;
-	device_start = i;
-
-	/* mark home slots of cartridges currently on drives as non free */
-
-	for (i = device_start; i < device_start+robot_info->device_count; i++) {
-		if (((element_info+i)->state & 0x1) == 0) continue;
-		for (j = slot_start; j < slot_start+robot_info->slot_count; j++)
-			if ((element_info+i)->source_address ==
-				(element_info+j)->element_address) break;
-		(element_info+j)->state |= 1;
-	}
-
-	/* loop on all import slots */
-
-	for (i = port_start; i < port_start+robot_info->port_count; i++) {
-		if (*vid && strcmp (vid, (element_info+i)->name)) continue;
-		if (*vid || (*vid == '\0' && ((element_info+i)->state & 2))) {
-
-			/* find a free storage slot */
-
-			for (j = slot_start; j < slot_start+robot_info->slot_count; j++)
-				if (((element_info+j)->state & 0x1) == 0) break;
-			if (j >= slot_start+robot_info->slot_count) {
-				usrmsg (func, SR015);
-				free (element_info);
-				return (RBT_NORETRY);
-			}
-
-			if ((c = smc_move_medium (fd, loader, (element_info+i)->element_address,
-			    (element_info+j)->element_address, 0)) < 0) {
-				c = smc_lasterror (&smc_status, &msgaddr);
-				usrmsg (func, SR017, "import",
-				    (element_info+i)->name, msgaddr);
-				free (element_info);
-				return (c);
-			}
-			if (*vid || c) break;
-			(element_info+j)->state |= 1;	/* dest slot is now full */
-		}
-	}
-	free (element_info);
-	return (c);
-}
-
-int smc_mount (int fd,
-               char *loader,
-               struct robot_info *robot_info,
-               int drvord,
-               char *vid,
-               int invert)
-{
-    int c;
-    struct smc_element_info element_info;
-	char func[16];
-	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)) < 0) {
-		c = smc_lasterror (&smc_status, &msgaddr);
-		usrmsg (func, SR017, "find_cartridge", vid, msgaddr);
-		return (c);
-	}
-	if (c == 0) {
-		usrmsg (func, SR018, "mount", vid, drvord, "volume not in library");
-		return (RBT_NORETRY);
-	}
-	if (element_info.element_type != 2) {
-
-                /* compare requested and replied vid   */
-                usrmsg( 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:
-                        usrmsg( 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:
-                        usrmsg( func, "Location: import/export element (0x%x)\n", 
-                                element_info.element_type );
-                        break;
-                case 4:
-                        usrmsg( func, "Location: data transfer element (0x%x)\n", 
-                                element_info.element_type );
-                        break;
-                default:
-                        usrmsg( func, "Location: unknown (0x%x)\n", 
-                                element_info.element_type );
-                }
-
-                usrmsg (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);
-		usrmsg (func, SR018, "mount", vid, drvord, msgaddr);
-		return (c);
-	}
-	return (0);
-}
diff --git a/rmc/usrmsg.c b/rmc/usrmsg.c
deleted file mode 100644
index 6a1dc62074e9e20755ae2d58175af1dbf2ce1c3d..0000000000000000000000000000000000000000
--- a/rmc/usrmsg.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 1990-2002 by CERN/IT/PDP/DM
- * All rights reserved
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <stdarg.h>
-#include "rmc.h"
-#include <Ctape_api.h>
-
-int usrmsg(const char *func, const char *const msg, ...)
-{
-	va_list args;
-	char *p;
-	char prtbuf[PRTBUFSZ];
-	extern int rpfd;
-	int save_errno;
-
-	save_errno = errno;
-	va_start (args, msg);
-	sprintf (prtbuf, "%s: ", func);
-	p = prtbuf + strlen (prtbuf);
-	vsprintf (p, msg, args);
-	sendrep (rpfd, MSG_ERR, "%s", prtbuf);
-	va_end (args);
-	errno = save_errno;
-	return (0);
-}
diff --git a/tape/rbtsubr.c b/tape/rbtsubr.c
index a7f1d610062c6d0cf17f72b2ba8a5bd447bb1514..e6a977c331cdbf6689cc36750909fc7c989d29f4 100644
--- a/tape/rbtsubr.c
+++ b/tape/rbtsubr.c
@@ -24,7 +24,6 @@
 #include <arpa/inet.h>          /* arpa internet routines               */
 #include "h/net.h"
 #include "h/rmc_api.h"
-#include "h/smc.h"
 #include "h/Ctape.h"
 #include "h/Ctape_api.h"
 #include "h/tplogger_api.h"
diff --git a/tape/sendscsicmd.c b/tape/sendscsicmd.c
index 50851920c51ed1c4b89a9834cd219993a172bc57..c4803e8b618ba468d8453b412c266118ed7f2c0b 100644
--- a/tape/sendscsicmd.c
+++ b/tape/sendscsicmd.c
@@ -140,19 +140,20 @@ static void find_sgpath(char *sgpath, int maj, int min) {
 }
 
 
-int send_scsi_cmd (int tapefd,
-                   char *path,
-                   int do_not_open,
-                   unsigned char *cdb,
-                   int cdblen,
-                   unsigned char *buffer,
-                   int buflen,
-                   char *sense,
-                   int senselen,
-                   int timeout,   /* in milliseconds */
-                   int flags,
-                   int *nb_sense_ret,
-                   char **msgaddr)
+int 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 timeout,   /* in milliseconds */
+	const int flags,
+	int *const nb_sense_ret,
+	char **const msgaddr)
 {
 	int fd;
 	FILE *fopen();
diff --git a/test/unittest/Makefile b/test/unittest/Makefile
index 5b1843aaff558b3e45e76a31d81c6c760bc8bfc1..867683dd15dcbd9b377c7064facf91dffcd42f91 100644
--- a/test/unittest/Makefile
+++ b/test/unittest/Makefile
@@ -10,8 +10,10 @@ MACPORTS_CPPUNIT_INCLUDE_OP=
 MACPORTS_CPPUNIT_LIB_OP=
 endif
 
+CDK_OPS=-DLINUX -I/usr/include/CDK
+CDK_LIB_OPS = -L/usr/lib64/CDK -lapi -lcl -lipc -lutl
 ROOT_DIR=../..
-INCLUDE_OPS=-I$(ROOT_DIR) -I$(ROOT_DIR)/h $(MACPORTS_CPPUNIT_INCLUDE_OP)
+INCLUDE_OPS=-I$(ROOT_DIR) -I$(ROOT_DIR)/h $(MACPORTS_CPPUNIT_INCLUDE_OP) $(CDK_OPS)
 LIB_DIR_OPS=$(MACPORTS_CPPUNIT_LIB_OP)
 EXECUTABLES=rununittests
 CSEC_MECHLIST=KRB5 GSI ID
@@ -67,20 +69,26 @@ rununittests: \
   dlf_Dlf.o \
   dlf_Param.o \
   dlf_dlf_lib.o \
-  exception_Exception.o \
-  exception_InvalidArgument.o \
   exception_Communication.o \
+  exception_DismountFailed.o \
+  exception_Exception.o \
   exception_Internal.o \
+  exception_InvalidArgument.o \
   exception_InvalidConfigEntry.o \
   exception_InvalidConfiguration.o \
-  exception_TimeOut.o \
-  exception_TooBig.o \
+  exception_Mismatch.o \
+  exception_MissingOperand.o \
+  exception_MountFailed.o \
+  exception_NoEntry.o \
   exception_NoPortInRange.o \
   exception_NoValue.o \
-  exception_TapeNetAcceptInterrupted.o \
-  exception_PermissionDenied.o \
   exception_OutOfMemory.o \
-  exception_NoEntry.o \
+  exception_PermissionDenied.o \
+  exception_QueryVolumeFailed.o \
+  exception_RequestFailed.o \
+  exception_TapeNetAcceptInterrupted.o \
+  exception_TimeOut.o \
+  exception_TooBig.o \
   io_AbstractSocket.o \
   io_AbstractTCPSocket.o \
   io_AuthClientSocket.o \
@@ -118,6 +126,15 @@ rununittests: \
   metrics_InternalCounter.o \
   metrics_MetricsCollector.o \
   metrics_UpdateThread.o \
+  mediachanger_Acs.o \
+  mediachanger_AcsCmd.o \
+  mediachanger_DebugBuf.o \
+  mediachanger_DismountAcsCmdLine.o \
+  mediachanger_DismountAcsCmd.o \
+  mediachanger_MountAcsCmdLine.o \
+  mediachanger_MountAcsCmd.o \
+  mediachanger_QueryVolumeAcsCmdLine.o \
+  mediachanger_QueryVolumeAcsCmd.o \
   ns_Cns_selectsrvr.o \
   ns_Cns_opendir.o \
   ns_Cns_closedir.o \
@@ -310,13 +327,21 @@ rununittests: \
   tapegateway_VolumeRequest.o \
   test_exception.o \
   unittest.o \
+  unittest_AcsCmdTest.o \
+  unittest_AcsTest.o \
   unittest_CvtdenTest.o \
   unittest_InitlabelTest.o \
   unittest_Ctape_reserveTest.o \
+  unittest_DismountAcsCmdLineTest.o \
+  unittest_DismountAcsCmdTest.o \
   unittest_RecvTapeBridgeFlushedToTapeAckTest.o \
   unittest_MarshallTapeBridgeFlushedToTapeTest.o \
   unittest_SendTapeBridgeFlushedToTapeTest.o \
   unittest_MarshallTapeBridgeClientInfo2Test.o \
+  unittest_MountAcsCmdLineTest.o \
+  unittest_MountAcsCmdTest.o \
+  unittest_QueryVolumeAcsCmdLineTest.o \
+  unittest_QueryVolumeAcsCmdTest.o \
   unittest_NetTest.o \
   unittest_FileToRecallTest.o \
   unittest_MigrationReportConnectionTest.o \
@@ -354,7 +379,7 @@ rununittests: \
   vmgr_vmgr_querypool.o \
   vmgr_send2vmgr.o \
   vmgr_vmgr_querytape_byte_u64.o
-	g++ $(COMMON_OPS) $(LIB_DIR_OPS) -lpthread -ldl $(UUID_LIB) -lcppunit -lz -o $@ $^
+	g++ $(COMMON_OPS) $(LIB_DIR_OPS) $(CDK_LIB_OPS) -lpthread -ldl $(UUID_LIB) -lcppunit -lz -o $@ $^
 
 runUnitTestsMain.o: runUnitTestsMain.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
@@ -488,9 +513,21 @@ dlf_Param.o: $(ROOT_DIR)/castor/dlf/Param.cpp
 dlf_dlf_lib.o: $(ROOT_DIR)/dlf/dlf_lib.c
 	gcc -g -pthread -c $(INCLUDE_OPS) -o $@ $^
 
+exception_DismountFailed.o: $(ROOT_DIR)/castor/exception/DismountFailed.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
 exception_Exception.o: $(ROOT_DIR)/castor/exception/Exception.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
 
+exception_Mismatch.o: $(ROOT_DIR)/castor/exception/Mismatch.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+exception_MissingOperand.o: $(ROOT_DIR)/castor/exception/MissingOperand.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+exception_MountFailed.o: $(ROOT_DIR)/castor/exception/MountFailed.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
 exception_NoEntry.o: $(ROOT_DIR)/castor/exception/NoEntry.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
 
@@ -511,6 +548,12 @@ exception_InvalidConfiguration.o: \
   $(ROOT_DIR)/castor/exception/InvalidConfiguration.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
 
+exception_QueryVolumeFailed.o: $(ROOT_DIR)/castor/exception/QueryVolumeFailed.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+exception_RequestFailed.o: $(ROOT_DIR)/castor/exception/RequestFailed.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
 exception_TimeOut.o: $(ROOT_DIR)/castor/exception/TimeOut.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
 
@@ -673,6 +716,33 @@ metrics_UpdateThread.o: \
   $(ROOT_DIR)/castor/metrics/UpdateThread.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
 
+mediachanger_Acs.o: $(ROOT_DIR)/castor/tape/mediachanger/Acs.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_AcsCmd.o: $(ROOT_DIR)/castor/tape/mediachanger/AcsCmd.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_DebugBuf.o: $(ROOT_DIR)/castor/tape/mediachanger/DebugBuf.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_DismountAcsCmdLine.o: $(ROOT_DIR)/castor/tape/mediachanger/DismountAcsCmdLine.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_DismountAcsCmd.o: $(ROOT_DIR)/castor/tape/mediachanger/DismountAcsCmd.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_MountAcsCmdLine.o: $(ROOT_DIR)/castor/tape/mediachanger/MountAcsCmdLine.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_MountAcsCmd.o: $(ROOT_DIR)/castor/tape/mediachanger/MountAcsCmd.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_QueryVolumeAcsCmdLine.o: $(ROOT_DIR)/castor/tape/mediachanger/QueryVolumeAcsCmdLine.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+mediachanger_QueryVolumeAcsCmd.o: $(ROOT_DIR)/castor/tape/mediachanger/QueryVolumeAcsCmd.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
 ns_Cns_selectsrvr.o: $(ROOT_DIR)/ns/Cns_selectsrvr.c
 	gcc $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
 
@@ -1485,9 +1555,6 @@ unittest_SendTapeBridgeFlushedToTapeTest.o: $(ROOT_DIR)/test/unittest/tapebridge
 unittest_MarshallTapeBridgeClientInfo2Test.o: $(ROOT_DIR)/test/unittest/tapebridge/MarshallTapeBridgeClientInfo2Test.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
  
-unittest_NetTest.o: $(ROOT_DIR)/test/unittest/castor/tape/net/NetTest.cpp
-	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
- 
 unittest_FileToRecallTest.o: $(ROOT_DIR)/test/unittest/castor/tape/tapebridge/FileToRecallTest.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
  
@@ -1500,6 +1567,33 @@ unittest_SessionErrorTest.o: $(ROOT_DIR)/test/unittest/castor/tape/tapebridge/Se
 unittest_ClientAddressLocalTest.o: $(ROOT_DIR)/test/unittest/castor/tape/tapebridge/ClientAddressLocalTest.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
  
+unittest_AcsCmdTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/AcsCmdTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+ 
+unittest_AcsTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/AcsTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+unittest_DismountAcsCmdLineTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/DismountAcsCmdLineTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+unittest_DismountAcsCmdTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/DismountAcsCmdTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+
+unittest_NetTest.o: $(ROOT_DIR)/test/unittest/castor/tape/net/NetTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+ 
+unittest_MountAcsCmdLineTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/MountAcsCmdLineTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+ 
+unittest_MountAcsCmdTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/MountAcsCmdTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+ 
+unittest_QueryVolumeAcsCmdLineTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdLineTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+ 
+unittest_QueryVolumeAcsCmdTest.o: $(ROOT_DIR)/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdTest.cpp
+	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
+ 
 unittest_FileToMigrateTest.o: $(ROOT_DIR)/test/unittest/castor/tape/tapebridge/FileToMigrateTest.cpp
 	g++ $(COMMON_OPS) -c $(INCLUDE_OPS) -o $@ $^
  
diff --git a/test/unittest/castor/tape/mediachanger/AcsCmdTest.cpp b/test/unittest/castor/tape/mediachanger/AcsCmdTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..075f80dd02cfd1185c529bc806b88680ac1f4d5f
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/AcsCmdTest.cpp
@@ -0,0 +1,53 @@
+/******************************************************************************
+ *                test/unittest/castor/tape/mountacs/AcsCmdTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "test/unittest/castor/tape/mediachanger/MockAcs.hpp"
+#include "test/unittest/castor/tape/mediachanger/TestingAcsCmd.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <sstream>
+#include <string.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class AcsCmdTest: public CppUnit::TestFixture {
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+  }
+
+  CPPUNIT_TEST_SUITE(AcsCmdTest);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(AcsCmdTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/AcsTest.cpp b/test/unittest/castor/tape/mediachanger/AcsTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ae95325c1b852f196f7f811b832cde1aebd30db
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/AcsTest.cpp
@@ -0,0 +1,251 @@
+/******************************************************************************
+ *             test/unittest/castor/tape/dismountacs/AcsTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "test/unittest/castor/tape/mediachanger/MockAcs.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class AcsTest: public CppUnit::TestFixture {
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+  }
+
+  void testStr2DriveIdWithValidDriveId() {
+    const std::string str = "111:112:113:114";
+    DRIVEID driveId;
+    MockAcs acs;
+    CPPUNIT_ASSERT_NO_THROW_MESSAGE("Calling str2DriveId with valid drive ID",
+      driveId = acs.str2DriveId(str));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      (ACS)111, driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      (LSM)112, driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      (PANEL)113, driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      (DRIVE)114, driveId.drive);
+  }
+
+  void testStr2DriveIdWithEmptyString() {
+    const std::string str = "";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with empty string",
+      acs.str2DriveId(str),
+      castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveWithPrecedingZeros() {
+    const std::string str = "011:012:013:014";
+    DRIVEID driveId;
+    MockAcs acs;
+    CPPUNIT_ASSERT_NO_THROW_MESSAGE("Calling str2DriveId with preceding zeros",
+      driveId = acs.str2DriveId(str));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      (ACS)11, driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      (LSM)12, driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      (PANEL)13, driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      (DRIVE)14, driveId.drive);
+  }
+
+  void testStr2DriveIdWithTooManyColons() {
+    const std::string str = "111:112:113:114:115";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with too many colons",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testDriveId2StrWithArbitrary3DigitValues() {
+    DRIVEID driveId;
+    driveId.panel_id.lsm_id.acs = (ACS)111;
+    driveId.panel_id.lsm_id.lsm = (LSM)112;
+    driveId.panel_id.panel = (PANEL)113;
+    driveId.drive = (DRIVE)114;
+    MockAcs acs;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing str()",
+      std::string("111:112:113:114"), acs.driveId2Str(driveId));
+  }
+
+  void testDriveId2StrWithArbitrary1DigitValues() {
+    DRIVEID driveId;
+    driveId.panel_id.lsm_id.acs = (ACS)1;
+    driveId.panel_id.lsm_id.lsm = (LSM)2;
+    driveId.panel_id.panel = (PANEL)3;
+    driveId.drive = (DRIVE)4;
+    MockAcs acs;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing str()",
+      std::string("001:002:003:004"), acs.driveId2Str(driveId));
+  }
+
+  void testStr2DriveIdWithTooLongAcs() {
+    const std::string str = "1111:112:113:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with too long ACS",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithTooLongLsm() {
+    const std::string str = "111:1122:113:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with too long LSM",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithTooLongPanel() {
+    const std::string str = "111:112:1133:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with too long panel",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithTooLongTransport() {
+    const std::string str = "111:112:113:1144";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with too long drive",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithNonNumericAcs() {
+    const std::string str = "acs:112:113:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with non-numeric ACS",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithNonNumericLsm() {
+    const std::string str = "111:lsm:113:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with non-numeric LSM",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithNonNumericPanel() {
+    const std::string str = "111:112:pan:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with non-numeric panel",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithNonNumericTransport() {
+    const std::string str = "111:112:113:tra";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Calling str2DriveId with non-numeric drive",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithEmptyAcs() {
+    const std::string str = ":112:113:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with empty ACS",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithEmptyLsm() {
+    const std::string str = "111::113:114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with empty LSM",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithEmptyPanel() {
+    const std::string str = "111:112::114";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with empty panel",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2DriveIdWithEmptyTransport() {
+    const std::string str = "111:112:113:";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2DriveId with empty drive",
+      acs.str2DriveId(str), castor::exception::InvalidArgument);
+  }
+
+  void testStr2VolidWithEmptyString() {
+    const std::string str = "";
+    VOLID volId;
+    MockAcs acs;
+    CPPUNIT_ASSERT_NO_THROW_MESSAGE("Calling str2Volid with empty string",
+      volId = acs.str2Volid(str));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Checking external_label is an empty string",
+      '\0', volId.external_label[0]);
+  }
+
+  void testStr2VolidWith6CharacterString() {
+    const std::string str = "123456";
+    VOLID volId;
+    MockAcs acs;
+    CPPUNIT_ASSERT_NO_THROW_MESSAGE("Calling str2Volid with 6 character string",
+      volId = acs.str2Volid(str));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Checking external_label",
+      str, std::string(volId.external_label));
+  }
+
+  void testStr2VolidWith7CharacterString() {
+    const std::string str = "1234567";
+    MockAcs acs;
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling str2Volid with 7 character string",
+      acs.str2Volid(str), castor::exception::InvalidArgument);
+  }
+
+  CPPUNIT_TEST_SUITE(AcsTest);
+  CPPUNIT_TEST(testStr2DriveIdWithValidDriveId);
+  CPPUNIT_TEST(testStr2DriveIdWithEmptyString);
+  CPPUNIT_TEST(testStr2DriveWithPrecedingZeros);
+  CPPUNIT_TEST(testStr2DriveIdWithTooManyColons);
+  CPPUNIT_TEST(testStr2DriveIdWithTooLongAcs);
+  CPPUNIT_TEST(testStr2DriveIdWithTooLongLsm);
+  CPPUNIT_TEST(testStr2DriveIdWithTooLongPanel);
+  CPPUNIT_TEST(testStr2DriveIdWithTooLongTransport);
+  CPPUNIT_TEST(testStr2DriveIdWithNonNumericAcs);
+  CPPUNIT_TEST(testStr2DriveIdWithNonNumericLsm);
+  CPPUNIT_TEST(testStr2DriveIdWithNonNumericPanel);
+  CPPUNIT_TEST(testStr2DriveIdWithNonNumericTransport);
+  CPPUNIT_TEST(testStr2DriveIdWithEmptyAcs);
+  CPPUNIT_TEST(testStr2DriveIdWithEmptyLsm);
+  CPPUNIT_TEST(testStr2DriveIdWithEmptyPanel);
+  CPPUNIT_TEST(testStr2DriveIdWithEmptyTransport);
+  CPPUNIT_TEST(testDriveId2StrWithArbitrary3DigitValues);
+  CPPUNIT_TEST(testDriveId2StrWithArbitrary1DigitValues);
+  CPPUNIT_TEST(testStr2VolidWith6CharacterString);
+  CPPUNIT_TEST(testStr2VolidWith7CharacterString);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(AcsTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/DismountAcsCmdLineTest.cpp b/test/unittest/castor/tape/mediachanger/DismountAcsCmdLineTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1af3f6ebd578fda6ebd3b0eea54001f7ee1dec8b
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/DismountAcsCmdLineTest.cpp
@@ -0,0 +1,80 @@
+/******************************************************************************
+ *             test/unittest/castor/tape/dismountacs/DismountAcsCmdLineTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/mediachanger/DismountAcsCmdLine.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class DismountAcsCmdLineTest: public CppUnit::TestFixture {
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+  }
+
+  void testConstructor() {
+    const DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing debug flag is initialised to FALSE",
+      false, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing force flag is initialised to FALSE",
+      (BOOLEAN)FALSE, cmdLine.force);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing help flag is initialised to FALSE",
+      false, cmdLine.help);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing timeout option is initialised to 0",
+      0, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing query option is initialised to 0",
+      0, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.panel_id.lsm_id.acs is initialised to 0",
+      0, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.panel_id.lsm_id.lsm is initialised to 0",
+      0, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.panel_id.panel is initialised to 0",
+      0, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.drive is initialised to 0",
+      0, (int)cmdLine.driveId.drive);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing volId is initialised to an empty string",
+      '\0', cmdLine.volId.external_label[0]);
+  }
+
+  CPPUNIT_TEST_SUITE(DismountAcsCmdLineTest);
+  CPPUNIT_TEST(testConstructor);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DismountAcsCmdLineTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/DismountAcsCmdTest.cpp b/test/unittest/castor/tape/mediachanger/DismountAcsCmdTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a2a06f1f6b3c27f44860fb7158893c03ec15013
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/DismountAcsCmdTest.cpp
@@ -0,0 +1,578 @@
+/******************************************************************************
+ *             test/unittest/castor/tape/mediachanger/DismountAcsCmdTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "test/unittest/castor/tape/mediachanger/MockAcs.hpp"
+#include "test/unittest/castor/tape/mediachanger/TestingDismountAcsCmd.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <list>
+#include <memory>
+#include <sstream>
+#include <string.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class DismountAcsCmdTest: public CppUnit::TestFixture {
+private:
+
+  struct Argcv {
+    int argc;
+    char **argv;
+    Argcv(): argc(0), argv(NULL) {
+    }
+  };
+  typedef std::list<Argcv*> ArgcvList;
+  ArgcvList m_argsList;
+
+  /**
+   * Creates a duplicate string usin the new operator.
+   */
+  char *dupString(const char *str) {
+    const size_t len = strlen(str);
+    char *duplicate = new char[len+1];
+    strncpy(duplicate, str, len);
+    duplicate[len] = '\0';
+    return duplicate;
+  }
+
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+    // Allow getopt_long to be called again
+    optind = 0;
+
+    for(ArgcvList::const_iterator itor = m_argsList.begin();
+      itor != m_argsList.end(); itor++) {
+      for(int i=0; i < (*itor)->argc; i++) {
+        delete[] (*itor)->argv[i];
+      }
+      delete[] (*itor)->argv;
+      delete *itor;
+    }
+  }
+
+  void testParceCmdLineWithNoArgs() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 1;
+    args->argv = new char *[2];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling parseCmdLine with no arguments",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::MissingOperand);
+  }
+
+  void testParceCmdLineWithOnlyVolId() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("VIDVID");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Calling parseCmdLine with only volume identfier",
+      cmd.parseCmdLine(args->argc, args->argv),
+        castor::exception::MissingOperand);
+  }
+
+  void testParceCmdLineWithShortForce() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("-f");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing force is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.force);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongForce() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("--force");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing force is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.force);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithShortHelp() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("-h");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing help is set after command line is parsed",
+      true, cmdLine.help);
+  }
+
+ void testParceCmdLineWithLongHelp() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("--help");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing help is set after command line is parsed",
+      true, cmdLine.help);
+  }
+
+  void testParceCmdLineWithVolIdAndDrive() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("VIDVID");
+    args->argv[2] = dupString("111:112:113:114");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW_MESSAGE("Testing valid volume identfier and drive",
+      cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing debug is not set",
+      false, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing help is not set",
+      false, cmdLine.help);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing query is set to the default",
+      10, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing timeout is set to the default",
+      600, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithShortDebug() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("-d");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing debug is set after command line is parsed",
+      true, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongDebug() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("--debug");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing debug is set after command line is parsed",
+      true, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithTooLongVolIdAndDrive() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("VIDVID7");
+    args->argv[2] = dupString("111:112:113:114");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE("Testing volume identfier that is too long",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithValidVolIdAndInvalidDrive() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("VIDVID");
+    args->argv[2] = dupString("INVALID_DRIVE");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithShortTimeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("-t");
+    args->argv[2] = dupString("2");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing timeout is set after command line is parsed",
+      2, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongTimeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("--timeout");
+    args->argv[2] = dupString("2");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing timeout is set after command line is parsed",
+      2, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWith0Timeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("--timeout");
+    args->argv[2] = dupString("0");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithShortQuery() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("-q");
+    args->argv[2] = dupString("1");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing query is set after command line is parsed",
+      1, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongQuery() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("--query");
+    args->argv[2] = dupString("1");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    DismountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing query is set after command line is parsed",
+      1, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWith0Query() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("dismountacs");
+    args->argv[1] = dupString("--query");
+    args->argv[2] = dupString("0");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingDismountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  CPPUNIT_TEST_SUITE(DismountAcsCmdTest);
+  CPPUNIT_TEST(testParceCmdLineWithNoArgs);
+  CPPUNIT_TEST(testParceCmdLineWithOnlyVolId);
+  CPPUNIT_TEST(testParceCmdLineWithShortForce);
+  CPPUNIT_TEST(testParceCmdLineWithLongForce);
+  CPPUNIT_TEST(testParceCmdLineWithShortHelp);
+  CPPUNIT_TEST(testParceCmdLineWithLongHelp);
+  CPPUNIT_TEST(testParceCmdLineWithVolIdAndDrive);
+  CPPUNIT_TEST(testParceCmdLineWithShortDebug);
+  CPPUNIT_TEST(testParceCmdLineWithLongDebug);
+  CPPUNIT_TEST(testParceCmdLineWithTooLongVolIdAndDrive);
+  CPPUNIT_TEST(testParceCmdLineWithValidVolIdAndInvalidDrive);
+  CPPUNIT_TEST(testParceCmdLineWithShortTimeout);
+  CPPUNIT_TEST(testParceCmdLineWithLongTimeout);
+  CPPUNIT_TEST(testParceCmdLineWith0Timeout);
+  CPPUNIT_TEST(testParceCmdLineWithShortQuery);
+  CPPUNIT_TEST(testParceCmdLineWithLongQuery);
+  CPPUNIT_TEST(testParceCmdLineWith0Query);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DismountAcsCmdTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/MockAcs.hpp b/test/unittest/castor/tape/mediachanger/MockAcs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..743297a9ecc7beeefd32839f629e59e6e183b5c4
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/MockAcs.hpp
@@ -0,0 +1,135 @@
+/******************************************************************************
+ *    test/unittest/castor/tape/mediachanger/MockAcs.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_MOCKACS_HPP
+#define TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_MOCKACS_HPP 1
+
+#include "castor/tape/mediachanger/AcsCmd.hpp"
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class MockAcs: public Acs {
+public:
+
+  /**
+   * Destructor.
+   */
+  ~MockAcs() throw() {
+  }
+
+  /**
+   * C++ wrapper around the acs_mount() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param lockId Lock identifier or 0 meaning no lock.
+   * @param volId The identifier of the volume to be mounted.
+   * @param driveId The ID of the drive into which the volume is to be mounted.
+   * @param readOnly Set to true to request the volume be mounted for read-only
+   * access.
+   * @param bypass Set to true to override the ACSLS verification of
+   * compatibility between the drive and the media type of the volume.
+   * @return status value returned by acs_mount().
+   */
+  STATUS mount(
+    const SEQ_NO seqNumber,
+    const LOCKID lockId,
+    const VOLID &volId,
+    const DRIVEID &driveId,
+    const BOOLEAN readOnly,
+    const BOOLEAN bypass)
+    throw() {
+    return STATUS_SUCCESS;
+  }
+
+  /**
+   * C++ wrapper around the acs_dismount() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param lockId Lock identifier or 0 meaning no lock.
+   * @param volId The identifier of the volume to be mounted.
+   * @param driveId The ID of the drive into which the volume is to be mounted.
+   * @param force Set to true if the dismount should be forced.  Forcing a
+   * dismount means dismounting the volume from the specified drive without
+   * checking the identifier of the volume.
+   * @return status value returned by acs_dismount().
+   */
+  STATUS dismount(
+    const SEQ_NO seqNumber,
+    const LOCKID lockId,
+    const VOLID &volId,
+    const DRIVEID &driveId,
+    const BOOLEAN force)
+    throw() {
+    return STATUS_SUCCESS;
+  }
+
+  /**
+   * C++ wrapper around the acs_response() function of the ACSLS C-API.
+   *
+   * @param timeout Time in seconds to wait for a response.  A value of -1
+   * means block indefinitely and an a value of 0 means poll for the existence
+   * of a response.
+   * @param seqNumber Output parameter.  If a response exists then seqNumber
+   * is set.
+   * @param reqId Output parameter.  For an acknowledge response reqId is set
+   * to the request identifier of the original request. For an intermediate or
+   * final response reqId will be set to 0.
+   * @param rType Output parameter.  Set to the type of the response.
+   * @param rBuf Output parameter.  Set to the response information.
+   * @return status value returned by acs_response().
+   */
+  STATUS response(
+    const int timeout,
+    SEQ_NO &seqNumber,
+    REQ_ID &reqId,
+    ACS_RESPONSE_TYPE &rType,
+    ALIGNED_BYTES rBuf) throw() {
+    return STATUS_SUCCESS;
+  }
+
+  /**
+   * C++ wrapper around the acs_query_volume() function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param volIds Array of the volume identifiers to be queried.
+   * @param count The number of volume identifiers contained iwthin the volId
+   * parameter.
+   * @return status value returned by acs_response().
+   */
+  STATUS queryVolume(
+    const SEQ_NO seqNumber,
+    VOLID (&volIds)[MAX_ID],
+    const unsigned short count) throw() {
+    return STATUS_SUCCESS;
+  }
+
+}; // class MockAcs
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
+
+#endif // TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_MOCKACS_HPP
diff --git a/test/unittest/castor/tape/mediachanger/MountAcsCmdLineTest.cpp b/test/unittest/castor/tape/mediachanger/MountAcsCmdLineTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a5ee1f06287f6ade7d351701cd922b572a5aa17
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/MountAcsCmdLineTest.cpp
@@ -0,0 +1,81 @@
+/******************************************************************************
+ *             test/unittest/castor/tape/dismountacs/MountAcsCmdLineTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/mediachanger/MountAcsCmdLine.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class MountAcsCmdLineTest: public CppUnit::TestFixture {
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+  }
+
+  void testConstructor() {
+    const MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing debug flag is initialised to FALSE",
+      false, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing help flag is initialised to FALSE",
+      false, cmdLine.help);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing readOnly flag is initialised to FALSE",
+      (BOOLEAN)FALSE, cmdLine.readOnly);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing timeout option is initialised to 0",
+      0, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing query option is initialised to 0",
+      0, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.panel_id.lsm_id.acs is initialised to 0",
+      0, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.panel_id.lsm_id.lsm is initialised to 0",
+      0, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.panel_id.panel is initialised to 0",
+      0, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing driveId.drive is initialised to 0",
+      0, (int)cmdLine.driveId.drive);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing volId is initialised to an empty string",
+      '\0', cmdLine.volId.external_label[0]);
+  }
+
+  CPPUNIT_TEST_SUITE(MountAcsCmdLineTest);
+  CPPUNIT_TEST(testConstructor);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(MountAcsCmdLineTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/MountAcsCmdTest.cpp b/test/unittest/castor/tape/mediachanger/MountAcsCmdTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d8573d265259704032846e48a66fdc9a119aa29
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/MountAcsCmdTest.cpp
@@ -0,0 +1,578 @@
+/******************************************************************************
+ *             test/unittest/castor/tape/mediachanger/MountAcsCmdTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "test/unittest/castor/tape/mediachanger/MockAcs.hpp"
+#include "test/unittest/castor/tape/mediachanger/TestingMountAcsCmd.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <list>
+#include <memory>
+#include <sstream>
+#include <string.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class MountAcsCmdTest: public CppUnit::TestFixture {
+private:
+
+  struct Argcv {
+    int argc;
+    char **argv;
+    Argcv(): argc(0), argv(NULL) {
+    }
+  };
+  typedef std::list<Argcv*> ArgcvList;
+  ArgcvList m_argsList;
+
+  /**
+   * Creates a duplicate string usin the new operator.
+   */
+  char *dupString(const char *str) {
+    const size_t len = strlen(str);
+    char *duplicate = new char[len+1];
+    strncpy(duplicate, str, len);
+    duplicate[len] = '\0';
+    return duplicate;
+  }
+
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+    // Allow getopt_long to be called again
+    optind = 0;
+
+    for(ArgcvList::const_iterator itor = m_argsList.begin();
+      itor != m_argsList.end(); itor++) {
+      for(int i=0; i < (*itor)->argc; i++) {
+        delete[] (*itor)->argv[i];
+      }
+      delete[] (*itor)->argv;
+      delete *itor;
+    }
+  }
+
+  void testParceCmdLineWithNoArgs() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 1;
+    args->argv = new char *[2];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling parseCmdLine with no arguments",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::MissingOperand);
+  }
+
+  void testParceCmdLineWithOnlyVolId() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("VIDVID");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Calling parseCmdLine with only volume identfier",
+      cmd.parseCmdLine(args->argc, args->argv),
+        castor::exception::MissingOperand);
+  }
+
+  void testParceCmdLineWithShortReadOnly() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("-r");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing readOnly is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.readOnly);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongReadOnly() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("--readonly");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing readOnly is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.readOnly);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithShortHelp() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("-h");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing help is set after command line is parsed",
+      true, cmdLine.help);
+  }
+
+ void testParceCmdLineWithLongHelp() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("--help");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing help is set after command line is parsed",
+      true, cmdLine.help);
+  }
+
+  void testParceCmdLineWithVolIdAndDrive() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("VIDVID");
+    args->argv[2] = dupString("111:112:113:114");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW_MESSAGE("Testing valid volume identfier and drive",
+      cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing debug is not set",
+      false, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing help is not set",
+      false, cmdLine.help);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing query is set to the default",
+      10, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing timeout is set to the default",
+      600, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithShortDebug() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("-d");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing debug is set after command line is parsed",
+      true, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongDebug() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("--debug");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = dupString("111:112:113:114");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing debug is set after command line is parsed",
+      true, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithTooLongVolIdAndDrive() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("VIDVID7");
+    args->argv[2] = dupString("111:112:113:114");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE("Testing volume identfier that is too long",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithValidVolIdAndInvalidDrive() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("VIDVID");
+    args->argv[2] = dupString("INVALID_DRIVE");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithShortTimeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("-t");
+    args->argv[2] = dupString("2");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing timeout is set after command line is parsed",
+      2, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongTimeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("--timeout");
+    args->argv[2] = dupString("2");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing timeout is set after command line is parsed",
+      2, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWith0Timeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("--timeout");
+    args->argv[2] = dupString("0");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithShortQuery() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("-q");
+    args->argv[2] = dupString("1");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing query is set after command line is parsed",
+      1, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWithLongQuery() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("--query");
+    args->argv[2] = dupString("1");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    MountAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing query is set after command line is parsed",
+      1, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing ACS number",
+      111, (int)cmdLine.driveId.panel_id.lsm_id.acs);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing LSM number",
+      112, (int)cmdLine.driveId.panel_id.lsm_id.lsm);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing panel number",
+      113, (int)cmdLine.driveId.panel_id.panel);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing drive number",
+      114, (int)cmdLine.driveId.drive);
+  }
+
+  void testParceCmdLineWith0Query() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 5;
+    args->argv = new char *[6];
+    args->argv[0] = dupString("mountacs");
+    args->argv[1] = dupString("--query");
+    args->argv[2] = dupString("0");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = dupString("111:112:113:114");
+    args->argv[5] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingMountAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  CPPUNIT_TEST_SUITE(MountAcsCmdTest);
+  CPPUNIT_TEST(testParceCmdLineWithNoArgs);
+  CPPUNIT_TEST(testParceCmdLineWithOnlyVolId);
+  CPPUNIT_TEST(testParceCmdLineWithShortReadOnly);
+  CPPUNIT_TEST(testParceCmdLineWithLongReadOnly);
+  CPPUNIT_TEST(testParceCmdLineWithShortHelp);
+  CPPUNIT_TEST(testParceCmdLineWithLongHelp);
+  CPPUNIT_TEST(testParceCmdLineWithVolIdAndDrive);
+  CPPUNIT_TEST(testParceCmdLineWithShortDebug);
+  CPPUNIT_TEST(testParceCmdLineWithLongDebug);
+  CPPUNIT_TEST(testParceCmdLineWithTooLongVolIdAndDrive);
+  CPPUNIT_TEST(testParceCmdLineWithValidVolIdAndInvalidDrive);
+  CPPUNIT_TEST(testParceCmdLineWithShortTimeout);
+  CPPUNIT_TEST(testParceCmdLineWithLongTimeout);
+  CPPUNIT_TEST(testParceCmdLineWith0Timeout);
+  CPPUNIT_TEST(testParceCmdLineWithShortQuery);
+  CPPUNIT_TEST(testParceCmdLineWithLongQuery);
+  CPPUNIT_TEST(testParceCmdLineWith0Query);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(MountAcsCmdTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdLineTest.cpp b/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdLineTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c1d1d6ca8057b54aca404df43968dc24cc81ae0
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdLineTest.cpp
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *          test/unittest/castor/tape/dismountacs/QueryVolumeAcsCmdLineTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/mediachanger/QueryVolumeAcsCmdLine.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class QueryVolumeAcsCmdLineTest: public CppUnit::TestFixture {
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+  }
+
+  void testConstructor() {
+    const QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing debug flag is initialised to FALSE",
+      (BOOLEAN)FALSE, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing help flag is initialised to FALSE",
+      (BOOLEAN)FALSE, cmdLine.help);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing timeout option is initialised to 0",
+      0, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing query option is initialised to 0",
+      0, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing volId is initialised to an empty string",
+      '\0', cmdLine.volId.external_label[0]);
+  }
+
+  CPPUNIT_TEST_SUITE(QueryVolumeAcsCmdLineTest);
+  CPPUNIT_TEST(testConstructor);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(QueryVolumeAcsCmdLineTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdTest.cpp b/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb510c1947eefa4a10c0718f71e4ff5cf5fb0d36
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdTest.cpp
@@ -0,0 +1,402 @@
+/******************************************************************************
+ *           test/unittest/castor/tape/mediachanger/QueryVolumeAcsCmdTest.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#include "test/unittest/castor/tape/mediachanger/MockAcs.hpp"
+#include "test/unittest/castor/tape/mediachanger/TestingQueryVolumeAcsCmd.hpp"
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <list>
+#include <memory>
+#include <sstream>
+#include <string.h>
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class QueryVolumeAcsCmdTest: public CppUnit::TestFixture {
+private:
+
+  struct Argcv {
+    int argc;
+    char **argv;
+    Argcv(): argc(0), argv(NULL) {
+    }
+  };
+  typedef std::list<Argcv*> ArgcvList;
+  ArgcvList m_argsList;
+
+  /**
+   * Creates a duplicate string usin the new operator.
+   */
+  char *dupString(const char *str) {
+    const size_t len = strlen(str);
+    char *duplicate = new char[len+1];
+    strncpy(duplicate, str, len);
+    duplicate[len] = '\0';
+    return duplicate;
+  }
+
+public:
+
+  void setUp() {
+  }
+
+  void tearDown() {
+    // Allow getopt_long to be called again
+    optind = 0;
+
+    for(ArgcvList::const_iterator itor = m_argsList.begin();
+      itor != m_argsList.end(); itor++) {
+      for(int i=0; i < (*itor)->argc; i++) {
+        delete[] (*itor)->argv[i];
+      }
+      delete[] (*itor)->argv;
+      delete *itor;
+    }
+  }
+
+  void testParceCmdLineWithNoArgs() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 1;
+    args->argv = new char *[2];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE("Calling parseCmdLine with no arguments",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::MissingOperand);
+  }
+
+  void testParceCmdLineWithValidVolId() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("VIDVID");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW_MESSAGE(
+      "Calling parseCmdLine with valid volume-identfier",
+      cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing debug is not set",
+      (BOOLEAN)FALSE, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing help is not set",
+      (BOOLEAN)FALSE, cmdLine.help);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing query is set to the default",
+      1, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing timeout is set to the default",
+      20, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+  }
+
+  void testParceCmdLineWithShortHelp() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("-h");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing help is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.help);
+  }
+
+ void testParceCmdLineWithLongHelp() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("--help");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing help is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.help);
+  }
+
+  void testParceCmdLineWithShortDebug() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("-d");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing debug is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+  }
+
+  void testParceCmdLineWithLongDebug() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 3;
+    args->argv = new char *[4];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("--debug");
+    args->argv[2] = dupString("VIDVID");
+    args->argv[3] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing debug is set after command line is parsed",
+      (BOOLEAN)TRUE, cmdLine.debug);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+  }
+
+  void testParceCmdLineWithTooLongVolId() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 2;
+    args->argv = new char *[3];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("VIDVID7");
+    args->argv[2] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE("Testing volume identfier that is too long",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithShortTimeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("-t");
+    args->argv[2] = dupString("2");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing timeout is set after command line is parsed",
+      2, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+  }
+
+  void testParceCmdLineWithLongTimeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("--timeout");
+    args->argv[2] = dupString("2");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing timeout is set after command line is parsed",
+      2, cmdLine.timeout);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+  }
+
+  void testParceCmdLineWith0Timeout() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("--timeout");
+    args->argv[2] = dupString("0");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  void testParceCmdLineWithShortQuery() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("-q");
+    args->argv[2] = dupString("1");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing query is set after command line is parsed",
+      1, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+  }
+
+  void testParceCmdLineWithLongQuery() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("--query");
+    args->argv[2] = dupString("1");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    QueryVolumeAcsCmdLine cmdLine;
+    CPPUNIT_ASSERT_NO_THROW(cmdLine = cmd.parseCmdLine(args->argc, args->argv));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(
+      "Testing query is set after command line is parsed",
+      1, cmdLine.queryInterval);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing volume identfier was parsed",
+      std::string("VIDVID"), std::string(cmdLine.volId.external_label));
+  }
+
+  void testParceCmdLineWith0Query() {
+    Argcv *args = new Argcv();
+    m_argsList.push_back(args);
+    args->argc = 4;
+    args->argv = new char *[5];
+    args->argv[0] = dupString("queryvolume");
+    args->argv[1] = dupString("--query");
+    args->argv[2] = dupString("0");
+    args->argv[3] = dupString("VIDVID");
+    args->argv[4] = NULL;
+
+    std::istringstream inStream;
+    std::ostringstream outStream;
+    std::ostringstream errStream;
+    MockAcs acs;
+    TestingQueryVolumeAcsCmd cmd(inStream, outStream, errStream, acs);
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+      "Testing valid volume identfier and invalid DRIVE",
+      cmd.parseCmdLine(args->argc, args->argv),
+      castor::exception::InvalidArgument);
+  }
+
+  CPPUNIT_TEST_SUITE(QueryVolumeAcsCmdTest);
+  CPPUNIT_TEST(testParceCmdLineWithNoArgs);
+  CPPUNIT_TEST(testParceCmdLineWithValidVolId);
+  CPPUNIT_TEST(testParceCmdLineWithShortHelp);
+  CPPUNIT_TEST(testParceCmdLineWithLongHelp);
+  CPPUNIT_TEST(testParceCmdLineWithShortDebug);
+  CPPUNIT_TEST(testParceCmdLineWithLongDebug);
+  CPPUNIT_TEST(testParceCmdLineWithTooLongVolId);
+  CPPUNIT_TEST(testParceCmdLineWithShortTimeout);
+  CPPUNIT_TEST(testParceCmdLineWithLongTimeout);
+  CPPUNIT_TEST(testParceCmdLineWith0Timeout);
+  CPPUNIT_TEST(testParceCmdLineWithShortQuery);
+  CPPUNIT_TEST(testParceCmdLineWithLongQuery);
+  CPPUNIT_TEST(testParceCmdLineWith0Query);
+  CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(QueryVolumeAcsCmdTest);
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
diff --git a/test/unittest/castor/tape/mediachanger/TestingAcs.hpp b/test/unittest/castor/tape/mediachanger/TestingAcs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8496694f997cf292b83998c618f93146f34747c8
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/TestingAcs.hpp
@@ -0,0 +1,92 @@
+/******************************************************************************
+ *    test/unittest/castor/tape/mediachanger/MockAcs.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_MOCKACS_HPP
+#define TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_MOCKACS_HPP 1
+
+#include "castor/tape/mediachanger/AcsCmd.hpp"
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class MockAcs: public Acs {
+public:
+
+  /**
+   * Destructor.
+   */
+  ~MockAcs() throw() {
+  }
+
+  /**
+   * C++ wrapper around the acs_mount function of the ACSLS C-API.
+   *
+   * @param seqNumber Client supplied sequence number.
+   * @param lockId Lock identifier or 0 meaning no lock.
+   * @param volId The volume ID of the tape to be mounted.
+   * @param driveId The ID of the drive into which the tape is to be mounted.
+   * @param readOnly Set to true to request the tape be mounted for read-only
+   * access.
+   * @param bypass Set to true to override the ACSLS verification of
+   * compatibility between the tape drive and the media type of the cartridge.
+   */
+  void mount(
+    const SEQ_NO seqNumber,
+    const LOCKID lockId,
+    const std::string &volId,
+    const DRIVEID &driveId,
+    const bool readOnly,
+    const bool bypass)
+    throw(castor::exception::Internal, castor::exception::MountFailed);
+
+  /**
+   * C++ wrapper around the acs_response function of the ACSLS C-API.
+   *
+   * @param timeout Time in seconds to wait for a response.  A value of -1
+   * means block indefinitely and an a value of 0 means poll for the existence
+   * of a response.
+   * @param seqNumber Output parameter.  If a response exists then seqNumber
+   * is set.
+   * @param reqId Output parameter.  For an acknowledge response reqId is set
+   * to the request identifier of the original request. For an intermediate or
+   * final response reqId will be set to 0.
+   * @param type Output parameter.  Set to the type of the response.
+   * @param buffer Output parameter.  Set to the response information.
+   * @return The status.
+   */
+  STATUS response(
+    const int timeout,
+    SEQ_NO &seqNumber,
+    REQ_ID &reqId,
+    ACS_RESPONSE_TYPE &type,
+    ALIGNED_BYTES *buffer)
+    throw();
+}; // class MockAcs
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
+
+#endif // TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_MOCKACS_HPP
diff --git a/test/unittest/castor/tape/mediachanger/TestingAcsCmd.hpp b/test/unittest/castor/tape/mediachanger/TestingAcsCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ff0a2fa9a8e68abb22d3c3c948736b9cd137ef7
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/TestingAcsCmd.hpp
@@ -0,0 +1,59 @@
+/******************************************************************************
+ *    test/unittest/castor/tape/mediachanger/TestingAcsCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGMOUNTACSCMD_HPP
+#define TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGMOUNTACSCMD_HPP 1
+
+#include "castor/tape/mediachanger/AcsCmd.hpp"
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class TestingAcsCmd: public AcsCmd {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  TestingAcsCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw():
+    AcsCmd(inStream, outStream, errStream, acs) {
+  }
+
+  ~TestingAcsCmd() throw() {
+  }
+
+}; // class TestingAcsCmd
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
+
+#endif // TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGMOUNTACSCMD_HPP
diff --git a/test/unittest/castor/tape/mediachanger/TestingAcsImpl.hpp b/test/unittest/castor/tape/mediachanger/TestingAcsImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..39dc01d959cb1711e9a97b21e26a1792c99292ad
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/TestingAcsImpl.hpp
@@ -0,0 +1,45 @@
+/******************************************************************************
+ *    test/unittest/castor/tape/mediachanger/TestingAcsImpl.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGACSIMPL_HPP
+#define TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGACSIMPL_HPP 1
+
+#include "castor/tape/mediachanger/AcsImpl.hpp"
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class TestingAcsImpl: public AcsImpl {
+public:
+
+  using AcsImpl::driveId2DRIVEID;
+
+}; // class TestingAcsImpl
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
+
+#endif // TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGACSIMPL_HPP
diff --git a/test/unittest/castor/tape/mediachanger/TestingDismountAcsCmd.hpp b/test/unittest/castor/tape/mediachanger/TestingDismountAcsCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..575cb7b0941fe12ac776e51e04410e198e5f0712
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/TestingDismountAcsCmd.hpp
@@ -0,0 +1,58 @@
+/******************************************************************************
+ *    test/unittest/castor/tape/mediachanger/TestingDismountAcsCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef TEST_UNITTEST_CASTOR_TAPE_DISMOUNTACS_TESTINGMOUNTACSCMD_HPP
+#define TEST_UNITTEST_CASTOR_TAPE_DISMOUNTACS_TESTINGMOUNTACSCMD_HPP 1
+
+#include "castor/tape/mediachanger/DismountAcsCmd.hpp"
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class TestingDismountAcsCmd: public DismountAcsCmd {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  TestingDismountAcsCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw():
+    DismountAcsCmd(inStream, outStream, errStream, acs) {
+  }
+
+  using DismountAcsCmd::parseCmdLine;
+
+}; // class TestingDismountAcsCmd
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
+
+#endif // TEST_UNITTEST_CASTOR_TAPE_DISMOUNTACS_TESTINGMOUNTACSCMD_HPP
diff --git a/test/unittest/castor/tape/mediachanger/TestingMountAcsCmd.hpp b/test/unittest/castor/tape/mediachanger/TestingMountAcsCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ead17ef0f5715d94dbf152ebda9fb4b7026d337
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/TestingMountAcsCmd.hpp
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *    test/unittest/castor/tape/mediachanger/TestingMountAcsCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGMOUNTACSCMD_HPP
+#define TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGMOUNTACSCMD_HPP 1
+
+#include "castor/tape/mediachanger/MountAcsCmd.hpp"
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class TestingMountAcsCmd: public MountAcsCmd {
+public:
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  TestingMountAcsCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw():
+    MountAcsCmd(inStream, outStream, errStream, acs) {
+  }
+
+  using MountAcsCmd::parseCmdLine;
+
+}; // class TestingMountAcsCmd
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
+
+#endif // TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGMOUNTACSCMD_HPP
diff --git a/test/unittest/castor/tape/mediachanger/TestingQueryVolumeAcsCmd.hpp b/test/unittest/castor/tape/mediachanger/TestingQueryVolumeAcsCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..68c53a8d062ba9519a57e3a420402b249bf0ab80
--- /dev/null
+++ b/test/unittest/castor/tape/mediachanger/TestingQueryVolumeAcsCmd.hpp
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *    test/unittest/castor/tape/mediachanger/TestingQueryVolumeAcsCmd.hpp
+ *
+ * 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 Steven.Murray@cern.ch
+ *****************************************************************************/
+
+#ifndef TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGQUERYVOLUMEACSCMD_HPP
+#define TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGQUERYVOLUMEACSCMD_HPP 1
+
+#include "castor/tape/mediachanger/QueryVolumeAcsCmd.hpp"
+
+namespace castor {
+namespace tape {
+namespace mediachanger {
+
+class TestingQueryVolumeAcsCmd: public QueryVolumeAcsCmd {
+public:
+  /**
+   * Constructor.
+   *
+   * @param inStream Standard input stream.
+   * @param outStream Standard output stream.
+   * @param errStream Standard error stream.
+   * @param acs Wrapper around the ACSLS C-API.
+   */
+  TestingQueryVolumeAcsCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw():
+    QueryVolumeAcsCmd(inStream, outStream, errStream, acs) {
+  }
+
+  using QueryVolumeAcsCmd::parseCmdLine;
+
+}; // class TestingQueryVolumeAcsCmd
+
+} // namespace mediachanger
+} // namespace tape
+} // namespace castor
+
+#endif // TEST_UNITTEST_CASTOR_TAPE_MEDIACHANGER_TESTINGQUERYVOLUMEACSCMD_HPP