diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4a07ba380338fcfa07491fb665dfa166485f98e1..f6d4197d4a87b936be48dba94238c9d54bcfa2a6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -60,6 +60,33 @@ cta_rpm:
   tags:
     - docker
 
+cta_rpm:
+  except:
+    - tags
+  stage: build:rpm
+  retry: 1
+  image: gitlab-registry.cern.ch/linuxsupport/cc7-base
+  script:
+    - cp -f continuousintegration/docker/ctafrontend/cc7/etc/yum.repos.d/*.repo /etc/yum.repos.d/
+    - yum install -y gcc-c++ cmake make rpm-build
+    - yum -y install yum-plugin-priorities yum-plugin-versionlock
+    - yum install -y git
+    - git submodule update --init --recursive
+    - cp -f continuousintegration/docker/ctafrontend/cc7/etc/yum/pluginconf.d/versionlock.list /etc/yum/pluginconf.d/
+    - yum-builddep --nogpgcheck -y build_srpm/RPM/SRPMS/*
+    - mkdir build_rpm
+    - cd build_rpm
+    - cmake -DVCS_VERSION=${CTA_BUILD_ID} ..
+    - make cta_rpm
+
+  artifacts:
+    expire_in: 30 days
+    paths:
+    - build_rpm/RPM/RPMS
+
+  tags:
+    - docker
+
 cta_tagged_rpm:
   only:
     - tags
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 61c68ce0ac1aeef96db89c970022d73f577d814f..078c2eaf7e631febd89ede318c4d15b33c1bc081 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -81,6 +81,7 @@ set (COMMON_LIB_SRC_FILES
   exception/DatabaseConstraintError.cpp  
   exception/DatabasePrimaryKeyError.cpp  
   exception/DismountFailed.cpp  
+  exception/ForceDismountFailed.cpp
   exception/Errnum.cpp
   exception/Exception.cpp
   exception/InvalidArgument.cpp
diff --git a/common/exception/ForceDismountFailed.cpp b/common/exception/ForceDismountFailed.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7bbec96aafcfbe9d96ce5aacfbe41ecfa1abc3b3
--- /dev/null
+++ b/common/exception/ForceDismountFailed.cpp
@@ -0,0 +1,34 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * 
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#include "ForceDismountFailed.hpp"
+//#include "serrno.h"
+
+
+// -----------------------------------------------------------------------------
+// Constructor
+// -----------------------------------------------------------------------------
+cta::exception::ForceDismountFailed::ForceDismountFailed():
+  cta::exception::Exception() {
+  //cta::exception::Exception(ETFDISMOUNTFAILED) {
+}
diff --git a/common/exception/ForceDismountFailed.hpp b/common/exception/ForceDismountFailed.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a05c62c12a23a50d9da5eef1106e57b2cd31aaf
--- /dev/null
+++ b/common/exception/ForceDismountFailed.hpp
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+#include "Exception.hpp"
+
+namespace cta {
+namespace exception {
+
+/**
+ * Failed to dismount volume.
+ */
+class ForceDismountFailed : public cta::exception::Exception {
+  
+public:
+  
+  /**
+   * Constructor
+   */
+  ForceDismountFailed();
+
+}; // class ForceDismountFailed
+
+} // namespace exception
+} // namespace cta
+
diff --git a/common/exception/QueryVolumeFailed.hpp b/common/exception/QueryVolumeFailed.hpp
index 09236633356079b1dfc309f5e340e37d8b6a8502..0143eb78083137ebf5d7c5bbe72f87c5882ed3ab 100644
--- a/common/exception/QueryVolumeFailed.hpp
+++ b/common/exception/QueryVolumeFailed.hpp
@@ -41,5 +41,5 @@ namespace cta { namespace exception {
 
     }; // class QueryVolumeFailed
 
-} } // namespace castor exception
+} } // namespace cta exception
 
diff --git a/common/log/IPAddress.hpp b/common/log/IPAddress.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1beccda1fdcdaeb9db7267aaea0efcf36782b30d
--- /dev/null
+++ b/common/log/IPAddress.hpp
@@ -0,0 +1,65 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *
+ * A simple object around an IP address
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+// Include Files
+#include <ostream>
+
+namespace cta {
+
+  namespace log {
+
+    /**
+     * A simple object around an IP address
+     */
+    class IPAddress {
+
+    public:
+
+      /**
+       * Constructor
+       */
+      IPAddress(int ip) : m_ip(ip) {};
+
+      /**
+       * Accessor
+       */
+      int ip() const { return m_ip; }
+
+    private:
+
+      /// the IP address, as an int
+      int m_ip;
+
+    };
+
+  } // end of namespace log
+
+} // end of namespace cta
+
+/**
+ * non-member operator to stream an IpAdress
+ */
+std::ostream& operator<<(std::ostream& out, const cta::log::IPAddress& ip);
diff --git a/common/log/TimeStamp.hpp b/common/log/TimeStamp.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3f13b957dc852936e646bf82b230072a184703b4
--- /dev/null
+++ b/common/log/TimeStamp.hpp
@@ -0,0 +1,67 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *
+ * A simple object around a time stamp
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+// Include Files
+#include <time.h>
+#include <ostream>
+
+namespace cta {
+
+  namespace log {
+
+    /**
+     * A simple object around a time stamp
+     */
+    class TimeStamp {
+
+    public:
+
+      /**
+       * Constructor
+       */
+      TimeStamp(time_t time) : m_time(time) {};
+
+      /**
+       * Accessor
+       */
+      int time() const { return m_time; }
+
+    private:
+
+      /// the IP address, as an int
+      int m_time;
+
+    };
+
+  } // end of namespace log
+
+} // end of namespace cta
+
+
+/**
+ * non-member operator to stream an IpAdress
+ */
+std::ostream& operator<<(std::ostream& out, const cta::log::TimeStamp& ts);
diff --git a/common/log/log.cpp b/common/log/log.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8f470019541d803626dd53ab8a007a46c332b1e
--- /dev/null
+++ b/common/log/log.cpp
@@ -0,0 +1,122 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *
+ * Interface to the CASTOR logging system
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#include "castor/exception/Exception.hpp"
+#include "castor/log/log.hpp"
+
+/**
+ * The logger to be used by the CASTOR logging systsem.
+ */
+static cta::log::Logger *s_logger = NULL;
+
+//------------------------------------------------------------------------------
+// init
+//------------------------------------------------------------------------------
+void cta::log::init(cta::log::Logger *logger) {
+  if(s_logger) {
+    throw cta::exception::Exception("Failed to initialise logging system"
+      ": Logging system already initialised");
+  }
+
+  s_logger = logger;
+}
+
+//------------------------------------------------------------------------------
+// shutdown
+//------------------------------------------------------------------------------
+void cta::log::shutdown() {
+  delete s_logger;
+  s_logger = NULL;
+}
+
+//------------------------------------------------------------------------------
+// instance
+//------------------------------------------------------------------------------
+cta::log::Logger &cta::log::instance() {
+  if(NULL == s_logger) {
+    throw cta::exception::Exception("Failed to get CASTOR logger"
+      ": Logger does not exist");
+  }
+  return *s_logger;
+}
+
+//------------------------------------------------------------------------------
+// prepareForFork
+//------------------------------------------------------------------------------
+void cta::log::prepareForFork() {
+  try {
+    instance().prepareForFork();
+  } catch(cta::exception::Exception &ex) {
+    throw cta::exception::Exception(
+     std::string("Failed to prepare logger for call to fork(): ") +
+       ex.getMessage().str());
+  }
+}
+
+//------------------------------------------------------------------------------
+// write
+//------------------------------------------------------------------------------
+void cta::log::write(
+  const int priority,
+  const std::string &msg,
+  const std::list<cta::log::Param> &params) {
+  if(s_logger) (*s_logger)(priority, msg, params);
+}
+
+//------------------------------------------------------------------------------
+// write
+//------------------------------------------------------------------------------
+void cta::log::write(
+  const int priority,
+  const std::string &msg,
+  const std::string &rawParams,
+  const struct timeval &timeStamp,
+  const std::string &progName,
+  const int pid) {
+  const std::list<Param> params;
+  if(s_logger) (*s_logger)(priority, msg, params, rawParams, timeStamp,
+    progName, pid);
+}
+
+//------------------------------------------------------------------------------
+// getProgramName
+//------------------------------------------------------------------------------
+std::string cta::log::getProgramName() {
+  if(s_logger) {
+    return (*s_logger).getProgramName();
+  } else {
+    return "";
+  }
+}
+
+//------------------------------------------------------------------------------
+// operator<<
+//------------------------------------------------------------------------------
+std::ostream& operator<<(std::ostream& out, const Cuuid_t& uuid) {
+  char uuidstr[CUUID_STRING_LEN + 1];
+  memset(uuidstr, '\0', CUUID_STRING_LEN + 1);
+  Cuuid2string(uuidstr, CUUID_STRING_LEN + 1, &uuid);
+  out << uuidstr;
+  return out;
+}
diff --git a/common/log/log.hpp b/common/log/log.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a5d57987bdd20df47a9aa2a8a71e25975401d90f
--- /dev/null
+++ b/common/log/log.hpp
@@ -0,0 +1,129 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *
+ * Interface to the CASTOR logging system
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+#include "Logger.hpp"
+#include "common/log/Param.hpp"
+#include "common/log/IPAddress.hpp"
+#include "common/log/TimeStamp.hpp"
+//#include "common/Cuuid.h"
+
+#include <list>
+#include <syslog.h>
+#include <sys/time.h>
+
+// more meaningful alias to NOTICE log level
+#define LOG_USER_ERROR LOG_NOTICE
+
+namespace cta {
+namespace log {
+
+  /**
+   * Initialises the logging system with the specified logger which should be
+   * allocated on the heap and will be owned by the logging system;
+   *
+   * This method is not thread safe.
+   *
+   * @logger The logger to be used by the logging system.
+   */
+  void init(cta::log::Logger *logger);
+
+  /**
+   * Deallocates the logger.
+   *
+   * This method is not thread safe.
+   */
+  void shutdown();
+
+  /**
+   * Returns a reference to the logger if it exists else throws an exception.
+   */
+  Logger &instance();
+
+  /**
+   * Prepares the logger object for a call to fork().
+   *
+   * No further calls to operator() should be made after calling this
+   * method until the call to fork() has completed.
+   */
+  void prepareForFork();
+
+  /**
+   * Writes a message into the CASTOR logging system. Note that no exception
+   * will ever be thrown in case of failure. Failures will actually be
+   * silently ignored in order to not impact the processing.
+   *
+   * Note that this version of write() implicitly uses the current time as
+   * the time stamp of the message.
+   *
+   * @param priority the priority of the message as defined by the syslog API.
+   * @param msg the message.
+   * @param params optionally the parameters of the message.
+   */
+  void write(
+    const int priority,
+    const std::string &msg,
+    const std::list<cta::log::Param> &params =
+      std::list<cta::log::Param>());
+
+  /**
+   * Writes a message into the CASTOR logging system. Note that no exception
+   * will ever be thrown in case of failure. Failures will actually be
+   * silently ignored in order to not impact the processing.
+   *
+   * Note that this version of write() is very specific and should not be
+   * used for general purpose. It allows the caller to specify the
+   * time stamp, the program name and the pid of the log message and
+   * takes preprocessed param
+   *
+   * @param priority the priority of the message as defined by the syslog API.
+   * @param msg the message.
+   * @param rawParams preprocessed parameters of the message.
+   * @param timeStamp the time stamp of the log message.
+   * @param progName the program name of the log message.
+   * @param pid the pid of the log message.
+   */
+  void write(
+    const int priority,
+    const std::string &msg,
+    const std::string &rawParams,
+    const struct timeval &timeStamp,
+    const std::string &progName,
+    const int pid);
+
+  /**
+   * Returns the program name if known or the empty string if not.
+   *
+   * @return the program name if known or the empty string if not.
+   */
+  std::string getProgramName();
+
+} // namespace log
+} // namespace cta
+
+/**
+ * non-member operator to stream a Cuuid_t
+ */
+//////std::ostream& operator<<(std::ostream& out, const Cuuid_t& uuid);
diff --git a/continuousintegration/docker/ctafrontend/cc7/etc/yum/pluginconf.d/versionlock.list b/continuousintegration/docker/ctafrontend/cc7/etc/yum/pluginconf.d/versionlock.list
index 9e7419eb1244605cf20d156482e2fbdb27ebf3c0..5184943b86b96e5a3f30f9d02fc05eabd9d337a5 100644
--- a/continuousintegration/docker/ctafrontend/cc7/etc/yum/pluginconf.d/versionlock.list
+++ b/continuousintegration/docker/ctafrontend/cc7/etc/yum/pluginconf.d/versionlock.list
@@ -1,17 +1,17 @@
-0:eos-archive-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-cleanup-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-client-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-debuginfo-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-fuse-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-fuse-core-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-fuse-sysv-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-fusex-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-fusex-core-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-fusex-selinux-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-server-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-srm-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-test-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
-0:eos-testkeytab-4.2.23-20180607211817gitc44922b.el7.cern.x86_64
+0:eos-archive-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-cleanup-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-client-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-debuginfo-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-fuse-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-fuse-core-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-fuse-sysv-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-fusex-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-fusex-core-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-fusex-selinux-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-server-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-srm-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-test-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
+0:eos-testkeytab-4.2.23-20180611183432git48f0e54.el7.cern.x86_64
 1:python2-xrootd-4.8.2-1.el7.*
 1:python3-xrootd-4.8.2-1.el7.*
 1:xrootd-4.8.2-1.el7.*
diff --git a/cta.spec.in b/cta.spec.in
index 8bd5052bf36479121b2dc105c7dea5c2cb562395..9240065686b1ddb10897ff710557e316ff747ef6 100644
--- a/cta.spec.in
+++ b/cta.spec.in
@@ -105,16 +105,13 @@ cd build
 Summary: CERN Tape Archive: tape daemon
 Group: Application/CTA
 Requires: logrotate
+Requires: cta-common = %{version}-%{release}
 Requires: cta-lib = %{version}-%{release}
-Requires(pre): /usr/bin/getent, /usr/sbin/groupadd, /usr/sbin/useradd
 %description -n cta-taped
 CERN Tape Archive:
 The tape server daemon
-%pre -n cta-taped
-/usr/bin/getent passwd cta || /usr/sbin/useradd -s /bin/nologin -c "CTA system account" -g tape cta
 %files -n cta-taped
 %defattr(-,root,root)
-%attr(0755,cta,cta) %dir /var/log/cta
 %attr(0644,root,root) %config(noreplace) /etc/logrotate.d/cta-taped
 %attr(0755,root,root) %{_bindir}/cta-taped
 %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cta/cta-taped.conf.example
@@ -138,21 +135,18 @@ The tape server daemon
 Summary: CERN Tape Archive: Xrootd plugin
 Group: Application/CTA
 Requires: logrotate
+Requires: cta-common = %{version}-%{release}
 Requires: cta-lib = %{version}-%{release}
 Requires: xrootd-server
-Requires(pre): /usr/bin/getent, /usr/sbin/groupadd, /usr/sbin/useradd
 %description -n cta-frontend
 CERN Tape Archive:
 The xroot plugin
-%pre -n cta-frontend
-/usr/bin/getent passwd cta || /usr/sbin/useradd -s /bin/nologin -c "CTA system account" -g tape cta
 %files -n cta-frontend
 %defattr(0755,root,root)
 %{_libdir}/libXrdSsiCta.so*
-%attr(0755,cta,cta) %dir /var/log/cta
 %attr(0644,root,root) %config(noreplace) /etc/logrotate.d/cta-frontend
 %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cta/cta-frontend-xrootd.conf
-%attr(0644,cta,cta) /etc/systemd/system/cta-frontend.service
+%attr(0644,cta,tape) /etc/systemd/system/cta-frontend.service
 
 #Frontend installs libraries so we need ldconfig.
 %post -n cta-frontend
@@ -212,6 +206,7 @@ The shared libraries
 Summary: CERN Tape Archive: unit and system tests with virtual tape drives
 Group: Application/CTA
 Requires: valgrind >= 3.8.1
+Requires: cta-lib = %{version}-%{release}
 Requires: cta-taped = %{ctaVersion}-%{ctaRelease}%{mydist}
 Requires: make
 %description -n cta-systemtests
@@ -241,6 +236,7 @@ Unit tests and system tests with virtual tape drives
 %{_libdir}/libctatapeserverscsiunittests.so*
 %{_libdir}/libctadaemonunittests.so*
 %{_libdir}/libctamediachangerunittests.so*
+%{_libdir}/libctamediachangeracsdaemonunittests.so*
 %{_bindir}/cta-systemTests
 %{_libdir}/libctadaemonunittests-multiprocess.so*
 %attr(0644,root,root) %{_datadir}/%{name}-%{ctaVersion}/unittest/*.suppr
@@ -249,6 +245,7 @@ Unit tests and system tests with virtual tape drives
 %package -n cta-objectstore-tools
 Summary: CERN Tape Archive: object store tools
 Group: Application/CTA
+Requires: cta-lib = %{version}-%{release}
 %description -n cta-objectstore-tools
 CERN Tape Archive:
 Tools allowing initialization and inspection of the object store.
@@ -267,6 +264,7 @@ Tools allowing initialization and inspection of the object store.
 %package -n cta-catalogueutils
 Summary: Utilities to faciliate working with the CTA catalogue
 Group: Application/CTA
+Requires: cta-lib = %{version}-%{release}
 %description -n cta-catalogueutils
 CERN Tape Archive:
 Scripts and utilities to faciliate working with the CTA catalogue
@@ -285,6 +283,7 @@ Scripts and utilities to faciliate working with the CTA catalogue
 %package -n cta-mediachangerutils
 Summary: Utilities to faciliate working with mediachangers
 Group: Application/CTA
+Requires: cta-lib = %{version}-%{release}
 %description -n cta-mediachangerutils
 CERN Tape Archive:
 Utilities to faciliate working with the mediachangers
@@ -294,21 +293,45 @@ Utilities to faciliate working with the mediachangers
 %attr(0644,root,root) %doc /usr/share/man/man1/cta-mediachanger-dismount.1cta.gz
 %attr(0644,root,root) %doc /usr/share/man/man1/cta-mediachanger-mount.1cta.gz
 
+%package -n cta-acsd
+Summary: Tools to faciliate working with acsd in cta
+Group: Application/CTA
+Requires: logrotate
+Requires: cta-common = %{version}-%{release}
+Requires: cta-lib = %{version}-%{release}
+%description -n cta-acsd
+CERN Tape Archive:
+Tools to faciliate working with acsd in cta
+%files -n cta-acsd
+%defattr(-,root,root)
+%attr(0644,root,root) %config(noreplace) /etc/logrotate.d/cta-acsd
+#%attr(0644,root,root) %doc /usr/share/man/man1/cta-acsd.1cta.gz
+%attr(0755,root,root) %{_bindir}/cta-acsd
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cta/cta-acsd.conf
+%attr(0644,root,root) %config(noreplace) /etc/sysconfig/cta-acsd
+%attr(0644,root,root) /etc/systemd/system/cta-acsd.service
+
+%post -n cta-acsd
+%systemd_post cta-acsd.service
+%systemdDaemonReload
+
+%preun -n cta-acsd
+%systemd_preun cta-acsd.service
+
+%postun -n cta-acsd
+%systemd_postun cta-acsd.service
+%systemdDaemonReload
 
 %package -n cta-rmcd
 Summary: Tools to faciliate working with rmcd and smc in cta
 Group: Application/CTA
 Requires: logrotate
-#Requires: cta-lib = %{version}-%{release}
-Requires(pre): /usr/bin/getent, /usr/sbin/groupadd, /usr/sbin/useradd
+Requires: cta-common = %{version}-%{release}
 %description -n cta-rmcd
 CERN Tape Archive:
 Tools to faciliate working with rmcd and smc in cta
-%pre -n cta-rmcd
-/usr/bin/getent passwd cta || /usr/sbin/useradd -s /bin/nologin -c "CTA system account" -g tape cta
 %files -n cta-rmcd
 %defattr(-,root,root)
-%attr(0755,cta,cta) %dir /var/log/cta
 %attr(0644,root,root) %config(noreplace) /etc/logrotate.d/cta-rmcd
 %attr(0644,root,root) %doc /usr/share/man/man1/cta-rmcd.1cta.gz
 %attr(0755,root,root) %{_bindir}/cta-rmcd
@@ -344,6 +367,20 @@ castor (Cern Advanced STORage system)
 %attr(0755,root,root) /usr/bin/cta-tape-acs-queryvolume
 %attr(0755,root,root) /usr/bin/cta-tape-acs-dismount
 %attr(0755,root,root) /usr/bin/cta-tape-acs-mount
+%attr(0755,root,root) /usr/bin/cta-tape-acs-querydrive
 %attr(0644,root,root) %doc /usr/share/man/man1/cta-tape-acs-queryvolume.1cta.gz
 %attr(0644,root,root) %doc /usr/share/man/man1/cta-tape-acs-mount.1cta.gz
 %attr(0644,root,root) %doc /usr/share/man/man1/cta-tape-acs-dismount.1cta.gz
+
+%package -n cta-common
+Summary: CERN Tape Archive common items
+Group: Application/CTA
+Requires(pre): /usr/bin/egrep, /usr/sbin/groupadd, /usr/sbin/luseradd
+%description -n cta-common
+CERN Tape Archive:
+Common items such as the creation of the cta local user and /var/log/cta
+%pre -n cta-common
+/usr/bin/egrep -q '^cta:' /etc/passwd || /usr/sbin/luseradd -s /bin/nologin -c "CTA system account" -g tape cta
+%files -n cta-common
+%defattr(-,root,root)
+%attr(0755,cta,tape) %dir /var/log/cta
diff --git a/mediachanger/AcsProxy.cpp b/mediachanger/AcsProxy.cpp
index 0244934662ac47d304fb548b05715e44210969df..cb5ec8cd8a0a10cab51a21a86fbc67b32dfd9ef4 100644
--- a/mediachanger/AcsProxy.cpp
+++ b/mediachanger/AcsProxy.cpp
@@ -197,7 +197,7 @@ void AcsProxy::mountTapeReadOnly(const std::string &vid, const LibrarySlot &libr
   } catch(cta::exception::Exception &ne) {
     cta::exception::Exception ex;
     ex.getMessage() <<
-      "Failed to request CASTOR ACS daemon to mount tape for read only access: "
+      "Failed to request CTA ACS daemon to mount tape for read only access: "
       << librarySlot.str() << ": " << ne.getMessage().str();
     throw ex;
   }
@@ -225,7 +225,7 @@ void AcsProxy::mountTapeReadWrite(const std::string &vid, const LibrarySlot &lib
   } catch(cta::exception::Exception &ne) {
     cta::exception::Exception ex;
     ex.getMessage() <<
-      "Failed to request CASTOR ACS daemon to mount tape for read/write " 
+      "Failed to request CTA ACS daemon to mount tape for read/write " 
       "access: " << librarySlot.str() << ": " << ne.getMessage().str();
     throw ex;
   }
@@ -253,7 +253,7 @@ void AcsProxy::dismountTape(const std::string &vid, const LibrarySlot &librarySl
   } catch(cta::exception::Exception &ne) {
     cta::exception::Exception ex;
     ex.getMessage() <<
-      "Failed to request CASTOR ACS daemon to dismount tape: " <<
+      "Failed to request CTA ACS daemon to dismount tape: " <<
       librarySlot.str() << ": " << ne.getMessage().str();
     throw ex;
   }
@@ -281,7 +281,7 @@ void AcsProxy::forceDismountTape(const std::string &vid, const LibrarySlot &libr
   } catch(cta::exception::Exception &ne) {
     cta::exception::Exception ex;
     ex.getMessage() <<
-      "Failed to request CASTOR ACS daemon to force dismount tape: " <<
+      "Failed to request CTA ACS daemon to force dismount tape: " <<
       librarySlot.str() << ": " << ne.getMessage().str();
     throw ex;
   }
diff --git a/mediachanger/CMakeLists.txt b/mediachanger/CMakeLists.txt
index 08602fd97f394e85084c5c44eb26462627ae2140..f5f23d67918edabb458d06d250fd055a93066b36 100644
--- a/mediachanger/CMakeLists.txt
+++ b/mediachanger/CMakeLists.txt
@@ -16,6 +16,7 @@
 cmake_minimum_required (VERSION 2.6)
 
 add_subdirectory(acs)
+add_subdirectory(reactor)
 add_subdirectory(castorrmc)
 find_package(openssl REQUIRED)
 find_package(Protobuf3 REQUIRED)
diff --git a/mediachanger/Exception.proto b/mediachanger/Exception.proto
new file mode 100644
index 0000000000000000000000000000000000000000..fd88601083069582cf39247d4df76ebee674a35b
--- /dev/null
+++ b/mediachanger/Exception.proto
@@ -0,0 +1,27 @@
+// 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
+syntax = "proto2";
+package cta.mediachanger;
+
+message Exception {
+  // The error code
+  required uint32 code = 1;
+
+  // The error message
+  required string message = 2;
+}
diff --git a/mediachanger/ReturnValue.proto b/mediachanger/ReturnValue.proto
new file mode 100644
index 0000000000000000000000000000000000000000..4f34c3ffbaab132f1f81ebc51caf90ccaa1dfd67
--- /dev/null
+++ b/mediachanger/ReturnValue.proto
@@ -0,0 +1,23 @@
+// 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
+syntax = "proto2";
+package cta.mediachanger;
+
+message ReturnValue {
+  required uint32 value = 1;
+}
diff --git a/mediachanger/acs/Acs.cpp b/mediachanger/acs/Acs.cpp
index d35112c3d43bcfd77324b3adc01521bb7ec2a451..ad7d46ba01c2a441edfdbe919c2badc0acfebae0 100644
--- a/mediachanger/acs/Acs.cpp
+++ b/mediachanger/acs/Acs.cpp
@@ -27,13 +27,13 @@
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::Acs::~Acs() throw() {
+cta::mediachanger::acs::Acs::~Acs() throw() {
 }
 
 //------------------------------------------------------------------------------
 // str2DriveId
 //------------------------------------------------------------------------------
-DRIVEID cta::acs::Acs::str2DriveId(const std::string &str) {
+DRIVEID cta::mediachanger::acs::Acs::str2DriveId(const std::string &str) {
   std::vector<std::string> components;
   cta::utils::splitString(str, ':', components);
 
@@ -112,7 +112,7 @@ DRIVEID cta::acs::Acs::str2DriveId(const std::string &str) {
 //------------------------------------------------------------------------------
 // onlyContainsNumerals
 //------------------------------------------------------------------------------
-bool cta::acs::Acs::onlyContainsNumerals(const std::string &str) throw() {
+bool cta::mediachanger::acs::Acs::onlyContainsNumerals(const std::string &str) throw() {
   for(std::string::const_iterator itor = str.begin(); itor != str.end();
     itor++) {
     if(*itor < '0' || *itor  > '9') {
@@ -125,7 +125,7 @@ bool cta::acs::Acs::onlyContainsNumerals(const std::string &str) throw() {
 //------------------------------------------------------------------------------
 // alpd2DriveId
 //------------------------------------------------------------------------------
-DRIVEID cta::acs::Acs::alpd2DriveId(const uint32_t acs,
+DRIVEID cta::mediachanger::acs::Acs::alpd2DriveId(const uint32_t acs,
   const uint32_t lsm, const uint32_t panel, const uint32_t drive) throw () {
   
   DRIVEID driveId;
@@ -140,7 +140,7 @@ DRIVEID cta::acs::Acs::alpd2DriveId(const uint32_t acs,
 //------------------------------------------------------------------------------
 // str2Volid
 //------------------------------------------------------------------------------
-VOLID cta::acs::Acs::str2Volid(const std::string &str) {
+VOLID cta::mediachanger::acs::Acs::str2Volid(const std::string &str) {
   if(EXTERNAL_LABEL_SIZE < str.length()) {
     cta::exception::InvalidArgument ex;
     ex.getMessage() << "Failed to convert string to volume identifier"
@@ -158,7 +158,7 @@ VOLID cta::acs::Acs::str2Volid(const std::string &str) {
 //------------------------------------------------------------------------------
 // driveId2Str
 //------------------------------------------------------------------------------
-std::string cta::acs::Acs::driveId2Str(const DRIVEID &driveId) throw() {
+std::string cta::mediachanger::acs::Acs::driveId2Str(const DRIVEID &driveId) throw() {
   std::ostringstream oss;
   oss << std::setfill('0') <<
     std::setw(3) << (int32_t)driveId.panel_id.lsm_id.acs << ":" <<
diff --git a/mediachanger/acs/Acs.hpp b/mediachanger/acs/Acs.hpp
index 33952de09a2eb618f285371592be3e3a4a69cc2b..bdd79bc01cca482b70cfff0f45796620ba851193 100644
--- a/mediachanger/acs/Acs.hpp
+++ b/mediachanger/acs/Acs.hpp
@@ -28,6 +28,7 @@ extern "C" {
 #include <stdint.h>
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -176,4 +177,5 @@ protected:
 }; // class  Acs
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsCmd.cpp b/mediachanger/acs/AcsCmd.cpp
index 4841fa2560431714f34efb7fd6da7a8ffa903367..83e1ea28bd83626af88d14bb549f101eabd344f1 100644
--- a/mediachanger/acs/AcsCmd.cpp
+++ b/mediachanger/acs/AcsCmd.cpp
@@ -23,7 +23,7 @@
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsCmd::AcsCmd(std::istream &inStream,
+cta::mediachanger::acs::AcsCmd::AcsCmd(std::istream &inStream,
   std::ostream &outStream, std::ostream &errStream, Acs &acs) throw():
   CmdLineTool(inStream, outStream, errStream), m_acs(acs) {
 }
@@ -31,13 +31,13 @@ cta::acs::AcsCmd::AcsCmd(std::istream &inStream,
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::AcsCmd::~AcsCmd() throw() {
+cta::mediachanger::acs::AcsCmd::~AcsCmd() throw() {
 }
 
 //------------------------------------------------------------------------------
 // bool2Str
 //------------------------------------------------------------------------------
-std::string cta::acs::AcsCmd::bool2Str(const BOOLEAN value) const
+std::string cta::mediachanger::acs::AcsCmd::bool2Str(const BOOLEAN value) const
   throw() {
   return value ? "TRUE" : "FALSE";
 }
@@ -45,7 +45,7 @@ std::string cta::acs::AcsCmd::bool2Str(const BOOLEAN value) const
 //------------------------------------------------------------------------------
 // requestResponsesUntilFinal
 //------------------------------------------------------------------------------
-void cta::acs::AcsCmd::requestResponsesUntilFinal(
+void cta::mediachanger::acs::AcsCmd::requestResponsesUntilFinal(
   const SEQ_NO requestSeqNumber,
   ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)],
   const int queryInterval, const int timeout) {
@@ -77,7 +77,7 @@ void cta::acs::AcsCmd::requestResponsesUntilFinal(
 //------------------------------------------------------------------------------
 // requestResponse
 //------------------------------------------------------------------------------
-ACS_RESPONSE_TYPE cta::acs::AcsCmd::requestResponse(
+ACS_RESPONSE_TYPE cta::mediachanger::acs::AcsCmd::requestResponse(
   const int timeout, const SEQ_NO requestSeqNumber,
   ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) {
   SEQ_NO responseSeqNumber = 0;
@@ -105,7 +105,7 @@ ACS_RESPONSE_TYPE cta::acs::AcsCmd::requestResponse(
 //------------------------------------------------------------------------------
 // checkSeqNumber
 //------------------------------------------------------------------------------
-void cta::acs::AcsCmd::checkResponseSeqNumber( const SEQ_NO requestSeqNumber, const SEQ_NO responseSeqNumber) {
+void cta::mediachanger::acs::AcsCmd::checkResponseSeqNumber( const SEQ_NO requestSeqNumber, const SEQ_NO responseSeqNumber) {
   if(requestSeqNumber != responseSeqNumber) {
     cta::exception::Mismatch ex;
     ex.getMessage() <<  ": Sequence number mismatch: requestSeqNumber="
diff --git a/mediachanger/acs/AcsCmd.hpp b/mediachanger/acs/AcsCmd.hpp
index 744a62bab35af862cd5eace4a7085b50b7d5fcbf..b57be3ab56e6c74b8edf8e76e6a1c7fffeb67b55 100644
--- a/mediachanger/acs/AcsCmd.hpp
+++ b/mediachanger/acs/AcsCmd.hpp
@@ -36,6 +36,7 @@ extern "C" {
 }
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -118,4 +119,5 @@ protected:
 }; // class AcsCmd
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsCmdLine.cpp b/mediachanger/acs/AcsCmdLine.cpp
index 1c4998166d7f12fe864760945b2c6e8f1ae3fb3b..9391a54256e0b286ca55f2ec990e5a400b34f3c1 100644
--- a/mediachanger/acs/AcsCmdLine.cpp
+++ b/mediachanger/acs/AcsCmdLine.cpp
@@ -26,7 +26,7 @@
 //------------------------------------------------------------------------------
 // parseQueryInterval
 //------------------------------------------------------------------------------
-int cta::acs::AcsCmdLine::parseQueryInterval(const std::string &s) {
+int cta::mediachanger::acs::AcsCmdLine::parseQueryInterval(const std::string &s) {
   const int queryInterval = atoi(s.c_str());
   if(0 >= queryInterval) {
     cta::exception::InvalidArgument ex;
@@ -41,7 +41,7 @@ int cta::acs::AcsCmdLine::parseQueryInterval(const std::string &s) {
 //------------------------------------------------------------------------------
 // parseTimeout
 //------------------------------------------------------------------------------
-int cta::acs::AcsCmdLine::parseTimeout(const std::string &s) {
+int cta::mediachanger::acs::AcsCmdLine::parseTimeout(const std::string &s) {
   const int timeout = atoi(s.c_str());
   if(0 >= timeout) {
     cta::exception::InvalidArgument ex;
@@ -55,7 +55,7 @@ int cta::acs::AcsCmdLine::parseTimeout(const std::string &s) {
 //------------------------------------------------------------------------------
 // handleMissingParameter
 //------------------------------------------------------------------------------
-void cta::acs::AcsCmdLine::handleMissingParameter(const int opt) {
+void cta::mediachanger::acs::AcsCmdLine::handleMissingParameter(const int opt) {
   cta::exception::MissingOperand ex;
   ex.getMessage() << "The -" << (char)opt << " option requires a parameter";
  throw ex;
@@ -64,7 +64,7 @@ void cta::acs::AcsCmdLine::handleMissingParameter(const int opt) {
 //------------------------------------------------------------------------------
 // handleUnknownOption
 //------------------------------------------------------------------------------
-void cta::acs::AcsCmdLine::handleUnknownOption(const int opt) {
+void cta::mediachanger::acs::AcsCmdLine::handleUnknownOption(const int opt) {
   cta::exception::InvalidArgument ex;
   if(0 == optopt) {
     ex.getMessage() << "Unknown command-line option";
diff --git a/mediachanger/acs/AcsCmdLine.hpp b/mediachanger/acs/AcsCmdLine.hpp
index 26455c6c02eabc3088e1a5731d729f50f106dac0..6fc41f90a62b6e7eea0ee619e18001edce3f237f 100644
--- a/mediachanger/acs/AcsCmdLine.hpp
+++ b/mediachanger/acs/AcsCmdLine.hpp
@@ -21,6 +21,7 @@
 #include <string>
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -61,4 +62,5 @@ protected:
 }; // class AcsCmdLine
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsDebugBuf.cpp b/mediachanger/acs/AcsDebugBuf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..850c0426bbd6b29d74ace92778524d1cb1f28c46
--- /dev/null
+++ b/mediachanger/acs/AcsDebugBuf.cpp
@@ -0,0 +1,69 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mediachanger/acs/AcsDebugBuf.hpp"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::DebugBuf::DebugBuf(std::ostream &os):
+  m_debug(false), m_os(os), m_writePreamble(true) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::DebugBuf::~DebugBuf() {
+}
+
+//------------------------------------------------------------------------------
+// setDebug
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::DebugBuf::setDebug(const bool value) throw() {
+  m_debug = value;
+}
+
+//------------------------------------------------------------------------------
+// overflow
+//------------------------------------------------------------------------------
+std::streambuf::int_type cta::mediachanger::acs::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 cta::mediachanger::acs::DebugBuf::writePreamble() throw() {
+  m_os << "DEBUG: ";
+}
diff --git a/mediachanger/acs/AcsDebugBuf.hpp b/mediachanger/acs/AcsDebugBuf.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c219337c093ec2b102f01bc79d109c8b1354f1b0
--- /dev/null
+++ b/mediachanger/acs/AcsDebugBuf.hpp
@@ -0,0 +1,95 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <streambuf>
+
+namespace cta {
+namespace mediachanger {
+namespace acs {
+
+/**
+ * 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 acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/AcsDismountCmd.cpp b/mediachanger/acs/AcsDismountCmd.cpp
index ede15c6d11c193cb4e2c643484953106a93732e5..209c5ece179cdfa62e5a8daf28c83b1f61364f4c 100644
--- a/mediachanger/acs/AcsDismountCmd.cpp
+++ b/mediachanger/acs/AcsDismountCmd.cpp
@@ -23,7 +23,7 @@
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsDismountCmd::AcsDismountCmd(
+cta::mediachanger::acs::AcsDismountCmd::AcsDismountCmd(
   std::istream &inStream, std::ostream &outStream, std::ostream &errStream,
   Acs &acs) throw():
   AcsCmd(inStream, outStream, errStream, acs) {
@@ -32,14 +32,14 @@ cta::acs::AcsDismountCmd::AcsDismountCmd(
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::AcsDismountCmd::~AcsDismountCmd() throw() {
+cta::mediachanger::acs::AcsDismountCmd::~AcsDismountCmd() throw() {
   // Do nothing
 }
 
 //------------------------------------------------------------------------------
 // exceptionThrowingMain
 //------------------------------------------------------------------------------
-int cta::acs::AcsDismountCmd::exceptionThrowingMain(const int argc,
+int cta::mediachanger::acs::AcsDismountCmd::exceptionThrowingMain(const int argc,
   char *const *const argv) {
   try {
     m_cmdLine = AcsDismountCmdLine(argc, argv);
@@ -74,7 +74,7 @@ int cta::acs::AcsDismountCmd::exceptionThrowingMain(const int argc,
 //------------------------------------------------------------------------------
 // syncDismount
 //------------------------------------------------------------------------------
-void cta::acs::AcsDismountCmd::syncDismount() {
+void cta::mediachanger::acs::AcsDismountCmd::syncDismount() {
   const SEQ_NO requestSeqNumber = 1;
   ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
 
@@ -94,7 +94,7 @@ void cta::acs::AcsDismountCmd::syncDismount() {
 //------------------------------------------------------------------------------
 // sendDismountRequest
 //------------------------------------------------------------------------------
-void cta::acs::AcsDismountCmd::sendDismountRequest(
+void cta::mediachanger::acs::AcsDismountCmd::sendDismountRequest(
   const SEQ_NO seqNumber) {
   const LOCKID lockId = 0; // No lock
 
@@ -115,7 +115,7 @@ void cta::acs::AcsDismountCmd::sendDismountRequest(
 //------------------------------------------------------------------------------
 // processDismountResponse
 //------------------------------------------------------------------------------
-void cta::acs::AcsDismountCmd::processDismountResponse(
+void cta::mediachanger::acs::AcsDismountCmd::processDismountResponse(
   ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) {
   const ACS_DISMOUNT_RESPONSE *const msg = (ACS_DISMOUNT_RESPONSE *)buf;
 
diff --git a/mediachanger/acs/AcsDismountCmd.hpp b/mediachanger/acs/AcsDismountCmd.hpp
index 3a9bb88f7e92fd0d4297383ea55b1c584c626831..e0db21b5d9afeb786cc86cdf5c9620f32a2d066b 100644
--- a/mediachanger/acs/AcsDismountCmd.hpp
+++ b/mediachanger/acs/AcsDismountCmd.hpp
@@ -25,6 +25,7 @@
 #include "common/exception/MissingOperand.hpp"
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -104,4 +105,5 @@ private:
 }; // class AcsDismountCmd
 
 } // namespace acs
+} // namepace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsDismountCmdLine.cpp b/mediachanger/acs/AcsDismountCmdLine.cpp
index 78668fd819bbf5d7373e391af9f4acdce33a2e98..cd37067422e81186dd7b06911be933d3596b3c68 100644
--- a/mediachanger/acs/AcsDismountCmdLine.cpp
+++ b/mediachanger/acs/AcsDismountCmdLine.cpp
@@ -30,7 +30,7 @@
 //-----------------------------------------------------------------------------
 // constructor
 //-----------------------------------------------------------------------------
-cta::acs::AcsDismountCmdLine::AcsDismountCmdLine() throw():
+cta::mediachanger::acs::AcsDismountCmdLine::AcsDismountCmdLine() throw():
   debug(false),
   force(FALSE),
   help(false),
@@ -46,7 +46,7 @@ cta::acs::AcsDismountCmdLine::AcsDismountCmdLine() throw():
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsDismountCmdLine::AcsDismountCmdLine(const int argc,
+cta::mediachanger::acs::AcsDismountCmdLine::AcsDismountCmdLine(const int argc,
   char *const *const argv):
   debug(false),
   force(FALSE),
@@ -108,7 +108,7 @@ cta::acs::AcsDismountCmdLine::AcsDismountCmdLine(const int argc,
 //------------------------------------------------------------------------------
 // processOption
 //------------------------------------------------------------------------------
-void cta::acs::AcsDismountCmdLine::processOption(const int opt) {
+void cta::mediachanger::acs::AcsDismountCmdLine::processOption(const int opt) {
   switch (opt) {
   case 'd':
     debug = true;
@@ -143,7 +143,7 @@ void cta::acs::AcsDismountCmdLine::processOption(const int opt) {
 //------------------------------------------------------------------------------
 // getUsage
 //------------------------------------------------------------------------------
-std::string cta::acs::AcsDismountCmdLine::getUsage() throw() {
+std::string cta::mediachanger::acs::AcsDismountCmdLine::getUsage() throw() {
   std::ostringstream usage;
   usage <<
   "Usage:\n"
diff --git a/mediachanger/acs/AcsDismountCmdLine.hpp b/mediachanger/acs/AcsDismountCmdLine.hpp
index 373955eb64aa007d6a2cce2b8e39f4033d98fdcd..c07f2a35225758e8f72d0c45507dcad2ca61f96b 100644
--- a/mediachanger/acs/AcsDismountCmdLine.hpp
+++ b/mediachanger/acs/AcsDismountCmdLine.hpp
@@ -28,6 +28,7 @@ extern "C" {
 #include <string>
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -109,4 +110,5 @@ private:
 }; // class AcsDismountCmdLine
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsDismountCmdMain.cpp b/mediachanger/acs/AcsDismountCmdMain.cpp
index cacd1534b6d1b92ae2bfba40d53c4489c6224bda..b01f3cf3886a472f4d58dfd09cd715ff78171d3c 100644
--- a/mediachanger/acs/AcsDismountCmdMain.cpp
+++ b/mediachanger/acs/AcsDismountCmdMain.cpp
@@ -62,8 +62,8 @@ int main(const int argc, char *const *const argv) {
 static int exceptionThrowingMain(const int argc, char *const *const argv) {
   using namespace cta;
 
-  acs::AcsImpl acs;
-  acs::AcsDismountCmd cmd(std::cin, std::cout, std::cerr, acs);
+  mediachanger::acs::AcsImpl acs;
+  mediachanger::acs::AcsDismountCmd cmd(std::cin, std::cout, std::cerr, acs);
 
   return cmd.exceptionThrowingMain(argc, argv);
 }
diff --git a/mediachanger/acs/AcsImpl.cpp b/mediachanger/acs/AcsImpl.cpp
index 3aa961b099c3188587e05a82e5f4e8b05a35536b..7ecf0aa8fc0970566f96d12188c6ef060905ed71 100644
--- a/mediachanger/acs/AcsImpl.cpp
+++ b/mediachanger/acs/AcsImpl.cpp
@@ -25,13 +25,13 @@
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::AcsImpl::~AcsImpl() throw() {
+cta::mediachanger::acs::AcsImpl::~AcsImpl() throw() {
 }
 
 //------------------------------------------------------------------------------
 // mount
 //------------------------------------------------------------------------------
-STATUS cta::acs::AcsImpl::mount(
+STATUS cta::mediachanger::acs::AcsImpl::mount(
   const SEQ_NO seqNumber,
   const LOCKID lockId,
   const VOLID &volId,
@@ -45,7 +45,7 @@ STATUS cta::acs::AcsImpl::mount(
 //------------------------------------------------------------------------------
 // dismount
 //------------------------------------------------------------------------------
-STATUS cta::acs::AcsImpl::dismount(
+STATUS cta::mediachanger::acs::AcsImpl::dismount(
   const SEQ_NO seqNumber,
   const LOCKID lockId,
   const VOLID &volId,
@@ -58,7 +58,7 @@ STATUS cta::acs::AcsImpl::dismount(
 //------------------------------------------------------------------------------
 // response
 //------------------------------------------------------------------------------
-STATUS cta::acs::AcsImpl::response(
+STATUS cta::mediachanger::acs::AcsImpl::response(
   const int timeout,
   SEQ_NO &seqNumber,
   REQ_ID &reqId,
@@ -70,7 +70,7 @@ STATUS cta::acs::AcsImpl::response(
 //------------------------------------------------------------------------------
 // queryVolume
 //------------------------------------------------------------------------------
-STATUS cta::acs::AcsImpl::queryVolume(
+STATUS cta::mediachanger::acs::AcsImpl::queryVolume(
   const SEQ_NO seqNumber,
   VOLID (&volIds)[MAX_ID],
   const unsigned short count) throw() {
diff --git a/mediachanger/acs/AcsImpl.hpp b/mediachanger/acs/AcsImpl.hpp
index 7adcb117a003879dc4e0719618680a83b59e7893..3fd93dcc1a7d7f63eb96fc38c900d840c1abb12c 100644
--- a/mediachanger/acs/AcsImpl.hpp
+++ b/mediachanger/acs/AcsImpl.hpp
@@ -21,6 +21,7 @@
 #include "Acs.hpp"
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -114,4 +115,5 @@ public:
 }; // class AcsImpl
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsLibraryInteraction.cpp b/mediachanger/acs/AcsLibraryInteraction.cpp
index c311ad9eef824e4e2e6b18eee25808e30da82a65..2cf7377ce89d79369dfc3e26d5669fd749604bac 100644
--- a/mediachanger/acs/AcsLibraryInteraction.cpp
+++ b/mediachanger/acs/AcsLibraryInteraction.cpp
@@ -18,28 +18,28 @@
  */
 
 #include "AcsLibraryInteraction.hpp"
-#include "../../log.hpp"
+#include "common/log/log.hpp"
 
 #include <stdlib.h>
 
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsLibraryInteraction::AcsLibraryInteraction(
-  Acs &acs) throw(): m_acs(acs) {
+cta::mediachanger::acs::AcsLibraryInteraction::AcsLibraryInteraction(
+  Acs &acs, cta::log::Logger& log) throw(): m_acs(acs), m_log(log) {
 }
 
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::AcsLibraryInteraction::~AcsLibraryInteraction() throw() {
+cta::mediachanger::acs::AcsLibraryInteraction::~AcsLibraryInteraction() throw() {
 }
 
 
 //------------------------------------------------------------------------------
 // requestResponsesUntilFinal
 //------------------------------------------------------------------------------
-void cta::acs::AcsLibraryInteraction::requestResponsesUntilFinal(
+void cta::mediachanger::acs::AcsLibraryInteraction::requestResponsesUntilFinal(
   const SEQ_NO requestSeqNumber,
   ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)],
   const int queryInterval, const int timeout) const
@@ -56,7 +56,7 @@ void cta::acs::AcsLibraryInteraction::requestResponsesUntilFinal(
     elapsedTime += time(NULL) - startTime;
 
     if(RT_ACKNOWLEDGE == responseType) {
-      log::write(LOG_DEBUG,"Received RT_ACKNOWLEDGE");
+      m_log(LOG_DEBUG,"Received RT_ACKNOWLEDGE");
     }
 
     if(elapsedTime >= timeout) {
@@ -66,13 +66,13 @@ void cta::acs::AcsLibraryInteraction::requestResponsesUntilFinal(
     }
   } while(RT_FINAL != responseType);
 
-  log::write(LOG_DEBUG,"Received RT_FINAL");
+  m_log(LOG_DEBUG,"Received RT_FINAL");
 }
 
 //------------------------------------------------------------------------------
 // requestResponse
 //------------------------------------------------------------------------------
-ACS_RESPONSE_TYPE cta::acs::AcsLibraryInteraction::requestResponse(
+ACS_RESPONSE_TYPE cta::mediachanger::acs::AcsLibraryInteraction::requestResponse(
   const int timeout, const SEQ_NO requestSeqNumber,
   ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const
    {
@@ -82,7 +82,7 @@ ACS_RESPONSE_TYPE cta::acs::AcsLibraryInteraction::requestResponse(
 
   std::stringstream dbgMsg;
   dbgMsg << "Calling Acs::response() for requestSeqNumber=" << requestSeqNumber;
-  log::write(LOG_DEBUG, dbgMsg.str());
+  m_log(LOG_DEBUG, dbgMsg.str());
   
   const STATUS s = m_acs.response(timeout, responseSeqNumber, reqId,
     responseType, buf);
@@ -91,7 +91,7 @@ ACS_RESPONSE_TYPE cta::acs::AcsLibraryInteraction::requestResponse(
   dbgMsg << "Acs::response() for requestSeqNumber=" << requestSeqNumber <<
     " returned responseSeqNumber=" << responseSeqNumber << " and status " << 
     acs_status(s);          
-  log::write(LOG_DEBUG,dbgMsg.str());
+  m_log(LOG_DEBUG,dbgMsg.str());
   
   switch(s) {
   case STATUS_SUCCESS:
@@ -109,7 +109,7 @@ ACS_RESPONSE_TYPE cta::acs::AcsLibraryInteraction::requestResponse(
 //------------------------------------------------------------------------------
 // checkSeqNumber
 //------------------------------------------------------------------------------
-void cta::acs::AcsLibraryInteraction::checkResponseSeqNumber(
+void cta::mediachanger::acs::AcsLibraryInteraction::checkResponseSeqNumber(
   const SEQ_NO requestSeqNumber, const SEQ_NO responseSeqNumber) const
    {
   if(requestSeqNumber != responseSeqNumber) {
@@ -123,7 +123,7 @@ void cta::acs::AcsLibraryInteraction::checkResponseSeqNumber(
 //------------------------------------------------------------------------------
 // volumeStatusAsString
 //------------------------------------------------------------------------------
-std::string cta::acs::AcsLibraryInteraction::volumeStatusAsString(
+std::string cta::mediachanger::acs::AcsLibraryInteraction::volumeStatusAsString(
   const QU_VOL_STATUS &s) const throw() {
   
   std::ostringstream os;
diff --git a/mediachanger/acs/AcsLibraryInteraction.hpp b/mediachanger/acs/AcsLibraryInteraction.hpp
index 363516364992a0657ca6d3c3a4540e27680a76a3..8dddcf591907df4e24c37e6f308b5f91d12dfb5f 100644
--- a/mediachanger/acs/AcsLibraryInteraction.hpp
+++ b/mediachanger/acs/AcsLibraryInteraction.hpp
@@ -20,6 +20,7 @@
 
 #include "common/exception/Mismatch.hpp"
 #include "common/exception/RequestFailed.hpp"
+#include "common/log/log.hpp"
 #include "Acs.hpp"
 
 #include <string>
@@ -30,6 +31,7 @@ extern "C" {
 }
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -44,7 +46,7 @@ public:
    *
    * @param acs Wrapper around the ACSLS C-API.
    */
-  AcsLibraryInteraction(Acs &acs) throw();
+  AcsLibraryInteraction(Acs &acs, cta::log::Logger &log) throw();
 
   /**
    * Pure-virtual destructor to guarantee this class is abstract.
@@ -108,10 +110,11 @@ protected:
    * Wrapper around the ACSLS C-API.
    */
   Acs &m_acs;
-  
+  log::Logger &m_log;
 }; // class AcsLibraryInteraction
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
 
 
diff --git a/mediachanger/acs/AcsMountCmd.cpp b/mediachanger/acs/AcsMountCmd.cpp
index 5ebf2950c9e4a9ef292a294e8478d145d56d2203..3ffa3a4aa2e8d792b01e11d28f60bc2e45a2f1db 100644
--- a/mediachanger/acs/AcsMountCmd.cpp
+++ b/mediachanger/acs/AcsMountCmd.cpp
@@ -18,14 +18,12 @@
 
 #include "AcsMountCmd.hpp"
 #include "AcsMountCmdLine.hpp"
-
 #include <getopt.h>
 #include <iostream>
- 
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsMountCmd::AcsMountCmd(
+cta::mediachanger::acs::AcsMountCmd::AcsMountCmd(
   std::istream &inStream, std::ostream &outStream, std::ostream &errStream,
   Acs &acs) throw():
   AcsCmd(inStream, outStream, errStream, acs) {
@@ -34,14 +32,14 @@ cta::acs::AcsMountCmd::AcsMountCmd(
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::AcsMountCmd::~AcsMountCmd() throw() {
+cta::mediachanger::acs::AcsMountCmd::~AcsMountCmd() throw() {
   // Do nothing
 }
 
 //------------------------------------------------------------------------------
 // exceptionThrowingMain
 //------------------------------------------------------------------------------
-int cta::acs::AcsMountCmd::exceptionThrowingMain(const int argc,
+int cta::mediachanger::acs::AcsMountCmd::exceptionThrowingMain(const int argc,
   char *const *const argv) {
   try {
     m_cmdLine = AcsMountCmdLine(argc, argv);
@@ -77,7 +75,7 @@ int cta::acs::AcsMountCmd::exceptionThrowingMain(const int argc,
 //------------------------------------------------------------------------------
 // syncMount
 //------------------------------------------------------------------------------
-void cta::acs::AcsMountCmd::syncMount() {
+void cta::mediachanger::acs::AcsMountCmd::syncMount() {
   const SEQ_NO requestSeqNumber = 1;
   ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
 
@@ -97,7 +95,7 @@ void cta::acs::AcsMountCmd::syncMount() {
 //------------------------------------------------------------------------------
 // sendMountRequest
 //------------------------------------------------------------------------------
-void cta::acs::AcsMountCmd::sendMountRequest(const SEQ_NO seqNumber) {
+void cta::mediachanger::acs::AcsMountCmd::sendMountRequest(const SEQ_NO seqNumber) {
   const LOCKID lockId = 0; // No lock
   const BOOLEAN bypass = FALSE;
 
@@ -119,7 +117,7 @@ void cta::acs::AcsMountCmd::sendMountRequest(const SEQ_NO seqNumber) {
 //------------------------------------------------------------------------------
 // processMountResponse
 //------------------------------------------------------------------------------
-void cta::acs::AcsMountCmd::processMountResponse(
+void cta::mediachanger::acs::AcsMountCmd::processMountResponse(
   ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)])
    {
   const ACS_MOUNT_RESPONSE *const msg = (ACS_MOUNT_RESPONSE *)buf;
diff --git a/mediachanger/acs/AcsMountCmd.hpp b/mediachanger/acs/AcsMountCmd.hpp
index 9aa38dec4d68bbab9f32b9a85892ec250c33a06e..46eb90ebf50ca35e0c9443108903c3f969e19ca5 100644
--- a/mediachanger/acs/AcsMountCmd.hpp
+++ b/mediachanger/acs/AcsMountCmd.hpp
@@ -27,6 +27,7 @@
 #include <stdint.h>
 
 namespace cta {
+namespace mediachanger    {
 namespace acs    {
 
 /**
@@ -100,4 +101,5 @@ private:
 }; // class AcsMountCmd
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsMountCmdLine.cpp b/mediachanger/acs/AcsMountCmdLine.cpp
index c33decac3c1f763ed02491643798a191e4c0b293..aa256c17205386bdb88823b8cd69e5680be4f47a 100644
--- a/mediachanger/acs/AcsMountCmdLine.cpp
+++ b/mediachanger/acs/AcsMountCmdLine.cpp
@@ -29,7 +29,7 @@
 //-----------------------------------------------------------------------------
 // constructor
 //-----------------------------------------------------------------------------
-cta::acs::AcsMountCmdLine::AcsMountCmdLine() throw():
+cta::mediachanger::acs::AcsMountCmdLine::AcsMountCmdLine() throw():
   debug(false),
   help(false),
   queryInterval(0),
@@ -45,7 +45,7 @@ cta::acs::AcsMountCmdLine::AcsMountCmdLine() throw():
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsMountCmdLine::AcsMountCmdLine(const int argc,
+cta::mediachanger::acs::AcsMountCmdLine::AcsMountCmdLine(const int argc,
   char *const *const argv):
   debug(false),
   help(false),
@@ -104,7 +104,7 @@ cta::acs::AcsMountCmdLine::AcsMountCmdLine(const int argc,
 //------------------------------------------------------------------------------
 // processOption
 //------------------------------------------------------------------------------
-void cta::acs::AcsMountCmdLine::processOption(const int opt) {
+void cta::mediachanger::acs::AcsMountCmdLine::processOption(const int opt) {
   switch(opt) {
   case 'd':
     debug = true;
@@ -139,7 +139,7 @@ void cta::acs::AcsMountCmdLine::processOption(const int opt) {
 //------------------------------------------------------------------------------
 // getUsage
 //------------------------------------------------------------------------------
-std::string cta::acs::AcsMountCmdLine::getUsage() throw() {
+std::string cta::mediachanger::acs::AcsMountCmdLine::getUsage() throw() {
   std::ostringstream usage;
   usage <<
   "Usage:\n"
diff --git a/mediachanger/acs/AcsMountCmdLine.hpp b/mediachanger/acs/AcsMountCmdLine.hpp
index e9abd303577b937f238d4d9cdab75622fa988ad5..3055432eada3a7e0cdf0b1b33a4fecc15a5c5d43 100644
--- a/mediachanger/acs/AcsMountCmdLine.hpp
+++ b/mediachanger/acs/AcsMountCmdLine.hpp
@@ -28,6 +28,7 @@ extern "C" {
 #include <string>
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -106,4 +107,5 @@ private:
 }; // class AcsMountCmdLine
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsMountCmdMain.cpp b/mediachanger/acs/AcsMountCmdMain.cpp
index 5c73a8e26903abc5df42c75b68f3760253cd456a..b1c4e965c72b2ef124d8a2a9774db947661f9a76 100644
--- a/mediachanger/acs/AcsMountCmdMain.cpp
+++ b/mediachanger/acs/AcsMountCmdMain.cpp
@@ -62,8 +62,8 @@ int main(const int argc, char *const *const argv) {
 static int exceptionThrowingMain(const int argc, char *const *const argv) {
   using namespace cta;
 
-  acs::AcsImpl acs;
-  acs::AcsMountCmd cmd(std::cin, std::cout, std::cerr, acs);
+  mediachanger::acs::AcsImpl acs;
+  mediachanger::acs::AcsMountCmd cmd(std::cin, std::cout, std::cerr, acs);
 
   return cmd.exceptionThrowingMain(argc, argv);
 }
diff --git a/mediachanger/acs/AcsQueryDriveCmd.cpp b/mediachanger/acs/AcsQueryDriveCmd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5978b01449241812aab2d955339091cdf5677ad
--- /dev/null
+++ b/mediachanger/acs/AcsQueryDriveCmd.cpp
@@ -0,0 +1,162 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "AcsQueryDriveCmd.hpp"
+#include "AcsQueryDriveCmdLine.hpp"
+#include "common/exception/QueryVolumeFailed.hpp"
+#include <getopt.h>
+#include <iostream>
+#include <string.h>
+ 
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::AcsQueryDriveCmd::AcsQueryDriveCmd(
+  std::istream &inStream, std::ostream &outStream, std::ostream &errStream,
+  Acs &acs) throw():
+  AcsCmd(inStream, outStream, errStream, acs) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::AcsQueryDriveCmd::~AcsQueryDriveCmd() throw() {
+  // Do nothing
+}
+
+//------------------------------------------------------------------------------
+// exceptionThrowingMain
+//------------------------------------------------------------------------------
+int cta::mediachanger::acs::AcsQueryDriveCmd::exceptionThrowingMain(const int argc,
+  char *const *const argv) {
+  try {
+    m_cmdLine = AcsQueryDriveCmdLine(argc, argv);
+  } catch(cta::exception::Exception &ex) {
+    m_err << ex.getMessage().str() << std::endl;
+    m_err << std::endl;
+    m_err << m_cmdLine.getUsage() << std::endl;
+    return 1;
+  }
+
+  // Display the usage message to standard out and exit with success if the
+  // user requested help
+  if(m_cmdLine.help) {
+    m_out << AcsQueryDriveCmdLine::getUsage();
+    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 << "DRIVE_SLOT = " << m_acs.driveId2Str(m_cmdLine.libraryDriveSlot);
+
+  syncQueryDrive();
+  return 0;
+}
+
+//------------------------------------------------------------------------------
+// syncQueryDrive
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::AcsQueryDriveCmd::syncQueryDrive() {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendQueryDriveRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, m_cmdLine.queryInterval,
+      m_cmdLine.timeout);
+    processQueryResponse(m_out, buf);
+  } catch(cta::exception::Exception &ex) {
+    cta::exception::QueryVolumeFailed qf;
+    qf.getMessage() << "Failed to query drive " <<
+      m_acs.driveId2Str(m_cmdLine.libraryDriveSlot) << ": " << ex.getMessage().str();
+    throw qf;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendQueryDriveRequest
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::AcsQueryDriveCmd::sendQueryDriveRequest(
+  const SEQ_NO sequence)  {
+
+  m_dbg << "Calling Acs::queryDrive()" << std::endl;
+  const STATUS s = acs_query_drive(sequence, &(m_cmdLine.libraryDriveSlot), 1);
+  m_dbg << "Acs::queryDrive() returned " << acs_status(s) << std::endl;
+
+  if(STATUS_SUCCESS != s) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Failed to send query request for drive " <<
+     m_acs.driveId2Str(m_cmdLine.libraryDriveSlot)<< ": " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// processQueryResponse
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::AcsQueryDriveCmd::processQueryResponse(
+  std::ostream &os,
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) {
+
+  const ACS_QUERY_DRV_RESPONSE *const msg = (ACS_QUERY_DRV_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->query_drv_status) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Status of query response is not success: " <<
+      acs_status(msg->query_drv_status);
+    throw ex;
+  }
+
+  if((unsigned short)1 != msg->count) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Query response does not contain a single drive: count="
+      << msg->count;
+    throw ex;
+  }
+
+  // count is 1 so it is safe to make a reference to the single drive status
+  const QU_DRV_STATUS &drvStatus = msg->drv_status[0];
+
+  if(m_acs.driveId2Str(m_cmdLine.libraryDriveSlot)!= m_acs.driveId2Str(drvStatus.drive_id)) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() <<
+      "Drive identifier of query response does not match that of request"
+      " requestDriveID=" <<m_acs.driveId2Str(m_cmdLine.libraryDriveSlot) <<
+      " responseDriveID=" << m_acs.driveId2Str(drvStatus.drive_id);
+    throw ex;
+  }
+
+  writeDriveStatus(os, drvStatus);
+}
+
+//------------------------------------------------------------------------------
+// writeDriveStatus
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::AcsQueryDriveCmd::writeDriveStatus(
+  std::ostream &os, const QU_DRV_STATUS &s) throw() {
+
+  os << "Drive identifier: " << m_acs.driveId2Str(s.drive_id) << std::endl;
+  os << "Drive type: " << acs_type((TYPE)s.drive_type) << std::endl;
+  os << "Drive state: " << acs_state(s.state) << std::endl;
+  os << "Drive status: " << acs_status(s.status) << std::endl;
+  os << "Volume identifier: " << s.vol_id.external_label << std::endl;
+}
diff --git a/mediachanger/acs/AcsQueryDriveCmd.hpp b/mediachanger/acs/AcsQueryDriveCmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d8d98379dcc511ba6d27cc1b70ac96db1f5eaa8d
--- /dev/null
+++ b/mediachanger/acs/AcsQueryDriveCmd.hpp
@@ -0,0 +1,117 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "AcsCmd.hpp"
+#include "AcsQueryDriveCmdLine.hpp"
+#include "common/exception/InvalidArgument.hpp"
+#include "common/exception/MissingOperand.hpp"
+#include "mediachanger/CmdLineTool.hpp"
+#include <stdint.h>
+
+namespace cta {
+namespace mediachanger {
+namespace acs {
+
+/**
+ * The class implementing the mount command.
+ */
+class AcsQueryDriveCmd: 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.
+   */
+  AcsQueryDriveCmd(std::istream &inStream, std::ostream &outStream,
+    std::ostream &errStream, Acs &acs) throw();
+
+  /**
+   * Destructor.
+   */
+  virtual ~AcsQueryDriveCmd() throw();
+
+  /**
+   * The entry function of the command.
+   *
+   * @param argc The number of command-line arguments.
+   * @param argv The command-line arguments.
+   * @return The exit value of the program.
+   */
+  int exceptionThrowingMain(const int argc, char *const *const argv);
+protected:
+
+  /**
+   * Queries ACS for information about the drive 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 drive status of the drive identifier specified on the
+   * command-line.
+   */
+  void syncQueryDrive();
+
+  /**
+   * Sends the query drive  request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendQueryDriveRequest(const SEQ_NO seqNumber);
+
+  /**
+   * Extracts the drive 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)]);
+
+  /**
+   * Writes a human readable representation of the specified drive status to
+   * the specified output stream.
+   *
+   * @param os The output stream.
+   * @param s The drive status.
+   */
+  void writeDriveStatus(std::ostream &os, const QU_DRV_STATUS &s) throw();
+
+private:
+
+  /**
+   * The parsed command-line.
+   *
+   * The value of this member variable is set within the main() method of this
+   * class.
+   */
+  AcsQueryDriveCmdLine m_cmdLine;
+
+}; // class AcsQueryDriveCmd
+
+} // namespace acs
+} // namepsace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/AcsQueryDriveCmdLine.cpp b/mediachanger/acs/AcsQueryDriveCmdLine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b74066efd1b9b04c99e68e949b8e8fd02e344c8f
--- /dev/null
+++ b/mediachanger/acs/AcsQueryDriveCmdLine.cpp
@@ -0,0 +1,157 @@
+
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Acs.hpp"
+#include "AcsQueryDriveCmdLine.hpp"
+#include "Constants.hpp"
+#include "common/exception/InvalidArgument.hpp"
+#include "common/exception/MissingOperand.hpp"
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+cta::mediachanger::acs::AcsQueryDriveCmdLine::AcsQueryDriveCmdLine() throw():
+  debug(false),
+  help(false),
+  queryInterval(0),
+  timeout(0) {
+  libraryDriveSlot.panel_id.lsm_id.acs = (ACS)0;
+  libraryDriveSlot.panel_id.lsm_id.lsm = (LSM)0;
+  libraryDriveSlot.panel_id.panel = (PANEL)0;
+  libraryDriveSlot.drive = (DRIVE)0;
+  memset(volId.external_label, '\0', sizeof(volId.external_label));
+}
+
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::AcsQueryDriveCmdLine::AcsQueryDriveCmdLine(const int argc,
+  char *const *const argv):
+  debug(false),
+  help(false),
+  queryInterval(ACS_QUERY_INTERVAL),
+  timeout(ACS_CMD_TIMEOUT) {
+  libraryDriveSlot.panel_id.lsm_id.acs = (ACS)0;
+  libraryDriveSlot.panel_id.lsm_id.lsm = (LSM)0;
+  libraryDriveSlot.panel_id.panel = (PANEL)0;
+  libraryDriveSlot.drive = (DRIVE)0;
+  memset(volId.external_label, '\0', sizeof(volId.external_label));
+
+  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}
+  };
+
+  // Prevent getopt() from printing an error message if it does not recognize
+  // an option character
+  opterr = 0;
+
+  int opt = 0;
+  while((opt = getopt_long(argc, argv, ":dhq:t:", longopts, NULL)) != -1) {
+    processOption(opt);
+  }
+  // There is no need to continue parsing when the help option is set
+  if(help) {
+    return;
+  }
+  // Check that DRIVE_SLOT has been specified
+  if(argc < 1) {
+    cta::exception::MissingOperand ex;
+    ex.getMessage() << "DRIVE_SLOT must be specified";
+    throw ex;
+  }
+  // Parse DRIVE_SLOT
+  libraryDriveSlot = Acs::str2DriveId(argv[1]);
+
+}
+//------------------------------------------------------------------------------
+// processOption
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::AcsQueryDriveCmdLine::processOption(const int opt) {
+  switch(opt) {
+  case 'd':
+    debug = true;
+    break;
+  case 'h':
+    help = true;
+    break;
+  case 'q':
+    queryInterval = parseQueryInterval(optarg);
+    break;
+  case 't':
+    timeout = parseTimeout(optarg);
+    break;
+  case ':':
+    return handleMissingParameter(optopt);
+  case '?':
+    return handleUnknownOption(optopt);
+  default:
+    {
+      cta::exception::Exception ex;
+      ex.getMessage() <<
+        "getopt_long returned the following unknown value: 0x" <<
+        std::hex << (int)opt;
+      throw ex;
+    }
+  } // switch(opt)
+}
+
+
+//------------------------------------------------------------------------------
+// getUsage
+//------------------------------------------------------------------------------
+  std::string cta::mediachanger::acs::AcsQueryDriveCmdLine::getUsage() throw() {
+  std::ostringstream usage;
+  usage <<
+  "Usage:\n"
+  "\n"
+  << getProgramName() << " [options] DRIVE_SLOT\n"
+  "\n"
+  "Where:\n"
+  "\n"
+  "  DRIVE_SLOT The slot in the tape library where the drive is located.\n"
+  "             DRIVE_SLOT must be in one of the following two forms:\n"
+  "\n"
+  "             acsACS_NUMBER,LSM_NUMBER,PANEL_NUMBER,TRANSPORT_NUMBER\n"
+  "             smcDRIVE_ORDINAL\n"
+  "\n"
+  "Options:\n"
+  "\n"
+  "  -d|--debug    Turn on the printing of debug information.\n"
+  "\n"
+  "  -h|--help     Print this help message and exit.\n"
+  "\n";
+  return usage.str();
+}
+
+//------------------------------------------------------------------------------
+// getProgramName
+//------------------------------------------------------------------------------
+std::string cta::mediachanger::acs::AcsQueryDriveCmdLine::getProgramName() {
+  return "cta-tape-acs-querydrive";
+}
+
diff --git a/mediachanger/acs/AcsQueryDriveCmdLine.hpp b/mediachanger/acs/AcsQueryDriveCmdLine.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d2f086f3320ec3df26466b49543a39745d1f685
--- /dev/null
+++ b/mediachanger/acs/AcsQueryDriveCmdLine.hpp
@@ -0,0 +1,118 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "AcsCmdLine.hpp"
+#include "mediachanger/AcsLibrarySlot.hpp"
+#include "mediachanger/LibrarySlotParser.hpp"
+
+extern "C" {
+#include "acssys.h"
+#include "acsapi.h"
+}
+
+#include <string>
+
+namespace cta {
+namespace mediachanger {
+namespace acs {
+
+/**
+ * Data type used to store the results of parsing the command-line.
+ */
+struct AcsQueryDriveCmdLine: public AcsCmdLine {
+  /**
+   * 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;
+
+  /**
+   * Constructor.
+   *
+   * Initialises all BOOLEAN member-variables to FALSE, all integer
+   * member-variables to 0 and the volume identifier to an empty string.
+   */
+  AcsQueryDriveCmdLine() throw();
+
+  /**
+   * Constructor.
+   *
+   * 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().
+   */
+  AcsQueryDriveCmdLine(const int argc, char *const *const argv);
+
+
+ /**
+  * Gets the usage message that describes the command line.
+  *
+  * @return The usage message.
+  */
+  static std::string getUsage() throw();
+
+  /**
+   * Return sthe program name.
+   *
+   * @return sthe program name.
+   */
+  static std::string getProgramName();
+
+  /**
+   * The slot in the tape library where the drive is located.
+   */
+
+  DRIVEID libraryDriveSlot;
+
+
+private:
+
+  /**
+   * Processes the specified option that was returned by getopt_long().
+   *
+   * @param opt The option that was returned by getopt_long().
+   */
+  void processOption(const int opt);
+
+}; // class AcsQueryDriveCmdLine
+
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/AcsQueryDriveCmdMain.cpp b/mediachanger/acs/AcsQueryDriveCmdMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cfc8d406ae0a9ffbe052a1c4e08456365be69000
--- /dev/null
+++ b/mediachanger/acs/AcsQueryDriveCmdMain.cpp
@@ -0,0 +1,60 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#include "AcsImpl.hpp"
+#include "AcsQueryDriveCmd.hpp"
+#include "AcsQueryDriveCmdLine.hpp"
+
+#include <iostream>
+
+/**
+ * An exception throwing version of main().
+ *
+ * @param argc The number of command-line arguments including the program name.
+ * @param argv The command-line arguments.
+ * @param The exit value of the program.
+ */ 
+static int exceptionThrowingMain(const int argc, char *const *const argv);
+  
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int main(const int argc, char *const *const argv) {
+  using namespace cta;
+  std::string errorMessage;
+  try {
+    return exceptionThrowingMain(argc, argv);
+  } catch(cta::exception::Exception &ex) {
+    errorMessage = ex.getMessage().str();
+  } catch(std::exception &se) {
+    errorMessage = se.what();
+  } catch(...) {
+    errorMessage = "An unknown exception was thrown";
+  }
+  return 1;
+}
+
+//------------------------------------------------------------------------------
+// exceptionThrowingMain
+//------------------------------------------------------------------------------
+static int exceptionThrowingMain(const int argc, char *const *const argv) {
+  using namespace cta;
+  mediachanger::acs::AcsImpl acs;
+  mediachanger::acs::AcsQueryDriveCmd cmd(std::cin, std::cout, std::cerr, acs);
+  return cmd.exceptionThrowingMain(argc, argv);
+}
diff --git a/mediachanger/acs/AcsQueryVolumeCmd.cpp b/mediachanger/acs/AcsQueryVolumeCmd.cpp
index 028a78a3384fc1a44c5f3e220f5f91b983f8b88d..7b3d64c543a51fd1f207042d560ad6e9ca92a119 100644
--- a/mediachanger/acs/AcsQueryVolumeCmd.cpp
+++ b/mediachanger/acs/AcsQueryVolumeCmd.cpp
@@ -27,7 +27,7 @@
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsQueryVolumeCmd::AcsQueryVolumeCmd(
+cta::mediachanger::acs::AcsQueryVolumeCmd::AcsQueryVolumeCmd(
   std::istream &inStream, std::ostream &outStream, std::ostream &errStream,
   Acs &acs) throw():
   AcsCmd(inStream, outStream, errStream, acs) {
@@ -36,14 +36,14 @@ cta::acs::AcsQueryVolumeCmd::AcsQueryVolumeCmd(
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::AcsQueryVolumeCmd::~AcsQueryVolumeCmd() throw() {
+cta::mediachanger::acs::AcsQueryVolumeCmd::~AcsQueryVolumeCmd() throw() {
   // Do nothing
 }
 
 //------------------------------------------------------------------------------
 // exceptionThrowingMain
 //------------------------------------------------------------------------------
-int cta::acs::AcsQueryVolumeCmd::exceptionThrowingMain(const int argc,
+int cta::mediachanger::acs::AcsQueryVolumeCmd::exceptionThrowingMain(const int argc,
   char *const *const argv) {
   try {
     m_cmdLine = AcsQueryVolumeCmdLine(argc, argv);
@@ -75,7 +75,7 @@ int cta::acs::AcsQueryVolumeCmd::exceptionThrowingMain(const int argc,
 //------------------------------------------------------------------------------
 // syncQueryVolume
 //------------------------------------------------------------------------------
-void cta::acs::AcsQueryVolumeCmd::syncQueryVolume() {
+void cta::mediachanger::acs::AcsQueryVolumeCmd::syncQueryVolume() {
   const SEQ_NO requestSeqNumber = 1;
   ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
 
@@ -95,7 +95,7 @@ void cta::acs::AcsQueryVolumeCmd::syncQueryVolume() {
 //------------------------------------------------------------------------------
 // sendQueryVolumeRequest
 //------------------------------------------------------------------------------
-void cta::acs::AcsQueryVolumeCmd::sendQueryVolumeRequest(
+void cta::mediachanger::acs::AcsQueryVolumeCmd::sendQueryVolumeRequest(
   const SEQ_NO seqNumber)  {
   VOLID volIds[MAX_ID];
 
@@ -119,7 +119,7 @@ void cta::acs::AcsQueryVolumeCmd::sendQueryVolumeRequest(
 //------------------------------------------------------------------------------
 // processQueryResponse
 //------------------------------------------------------------------------------
-void cta::acs::AcsQueryVolumeCmd::processQueryResponse(
+void cta::mediachanger::acs::AcsQueryVolumeCmd::processQueryResponse(
   std::ostream &os,
   ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) {
 
@@ -157,7 +157,7 @@ void cta::acs::AcsQueryVolumeCmd::processQueryResponse(
 //------------------------------------------------------------------------------
 // writeVolumeStatus
 //------------------------------------------------------------------------------
-void cta::acs::AcsQueryVolumeCmd::writeVolumeStatus(
+void cta::mediachanger::acs::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;
diff --git a/mediachanger/acs/AcsQueryVolumeCmd.hpp b/mediachanger/acs/AcsQueryVolumeCmd.hpp
index b5078f882bf60ec9084d8c0f093bd74011a444e9..7df2e22abe267f0a11c4eaf8eb090db6991b158b 100644
--- a/mediachanger/acs/AcsQueryVolumeCmd.hpp
+++ b/mediachanger/acs/AcsQueryVolumeCmd.hpp
@@ -28,6 +28,7 @@
 #include <stdint.h>
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -115,4 +116,5 @@ private:
 }; // class AcsQueryVolumeCmd
 
 } // namespace acs
+} // namepsace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsQueryVolumeCmdLine.cpp b/mediachanger/acs/AcsQueryVolumeCmdLine.cpp
index 351f5d22fe8c3b51bc70454bb24ba1d9b5e2cd2d..cbc4134d8fa470925e4c9c313517505cb9c9ed5d 100644
--- a/mediachanger/acs/AcsQueryVolumeCmdLine.cpp
+++ b/mediachanger/acs/AcsQueryVolumeCmdLine.cpp
@@ -30,7 +30,7 @@
 //-----------------------------------------------------------------------------
 // constructor
 //-----------------------------------------------------------------------------
-cta::acs::AcsQueryVolumeCmdLine::AcsQueryVolumeCmdLine()
+cta::mediachanger::acs::AcsQueryVolumeCmdLine::AcsQueryVolumeCmdLine()
   throw():
   debug(FALSE),
   help(FALSE),
@@ -42,7 +42,7 @@ cta::acs::AcsQueryVolumeCmdLine::AcsQueryVolumeCmdLine()
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::AcsQueryVolumeCmdLine::AcsQueryVolumeCmdLine(const int argc,
+cta::mediachanger::acs::AcsQueryVolumeCmdLine::AcsQueryVolumeCmdLine(const int argc,
   char *const *const argv):
   debug(FALSE),
   help(FALSE),
@@ -89,7 +89,7 @@ cta::acs::AcsQueryVolumeCmdLine::AcsQueryVolumeCmdLine(const int argc,
 //------------------------------------------------------------------------------
 // processOption
 //------------------------------------------------------------------------------
-void cta::acs::AcsQueryVolumeCmdLine::processOption(const int opt) {
+void cta::mediachanger::acs::AcsQueryVolumeCmdLine::processOption(const int opt) {
   switch(opt) {
   case 'd':
     debug = true;
@@ -121,7 +121,7 @@ void cta::acs::AcsQueryVolumeCmdLine::processOption(const int opt) {
 //------------------------------------------------------------------------------
 // getUsage
 //------------------------------------------------------------------------------
-std::string cta::acs::AcsQueryVolumeCmdLine::getUsage() throw() {
+std::string cta::mediachanger::acs::AcsQueryVolumeCmdLine::getUsage() throw() {
   std::ostringstream usage;
   usage <<
   "Usage:\n"
diff --git a/mediachanger/acs/AcsQueryVolumeCmdLine.hpp b/mediachanger/acs/AcsQueryVolumeCmdLine.hpp
index d327e760399515be826661069184bb9c5aefb8ad..733c64ed1a1f6e9b11796a0c1226a9124ce894cb 100644
--- a/mediachanger/acs/AcsQueryVolumeCmdLine.hpp
+++ b/mediachanger/acs/AcsQueryVolumeCmdLine.hpp
@@ -28,6 +28,7 @@ extern "C" {
 #include <string>
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -96,4 +97,5 @@ private:
 }; // class AcsQueryVolumeCmdLine
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/AcsQueryVolumeCmdMain.cpp b/mediachanger/acs/AcsQueryVolumeCmdMain.cpp
index ca65595934b013109838ae23910f325cd5176dc7..a9472eaf9479f311dacbe2ae1067553304cb31ef 100644
--- a/mediachanger/acs/AcsQueryVolumeCmdMain.cpp
+++ b/mediachanger/acs/AcsQueryVolumeCmdMain.cpp
@@ -61,8 +61,8 @@ int main(const int argc, char *const *const argv) {
 static int exceptionThrowingMain(const int argc, char *const *const argv) {
   using namespace cta;
 
-  acs::AcsImpl acs;
-  acs::AcsQueryVolumeCmd cmd(std::cin, std::cout, std::cerr, acs);
+  mediachanger::acs::AcsImpl acs;
+  mediachanger::acs::AcsQueryVolumeCmd cmd(std::cin, std::cout, std::cerr, acs);
 
   return cmd.exceptionThrowingMain(argc, argv);
 }
diff --git a/mediachanger/acs/CMakeLists.txt b/mediachanger/acs/CMakeLists.txt
index 42406cf47b53c4d52cd2859b9e18c813d04740b1..36438b4da17aec0c1aa114a9a0c0291371a07503 100644
--- a/mediachanger/acs/CMakeLists.txt
+++ b/mediachanger/acs/CMakeLists.txt
@@ -22,51 +22,24 @@
 # Rules to build and install acsd
 ################################################################################
 
-##add_library(
-  #castorAcsDaemon 
- ## Acs.cpp
-  #AcsDaemon.cpp
-  #AcsDaemonConfig.cpp
-  #AcsDaemonMain.cpp
-  #AcsDismountTape.cpp
-  #AcsForceDismountTape.cpp
- ## AcsImpl.cpp
-  #AcsLibraryInteraction.cpp
-  #AcsMessageHandler.cpp
-  #AcsMountTapeReadOnly.cpp
-  #AcsMountTapeReadWrite.cpp
-  #AcsPendingRequests.cpp
-  #AcsRequest.cpp
-  #AcsRequestDismountTape.cpp
-##)
-#add_dependencies(castorAcsDaemon castormessagesprotobuf)
+add_subdirectory(daemon)
 
-#target_link_libraries(castorAcsDaemon castortapereactor)
-#set_target_properties (castorAcsDaemon PROPERTIES
-#  COMPILE_FLAGS -I/usr/include/CDK
-#  COMPILE_DEFINITIONS LINUX)
-
-
-#add_executable(acsd AcsDaemon.cpp)
-#set_target_properties (acsd PROPERTIES
-#  COMPILE_FLAGS -I/usr/include/CDK
-#  COMPILE_DEFINITIONS LINUX)
+add_library(cta-acs 
+  Acs.cpp
+  AcsImpl.cpp
+  AcsDebugBuf.cpp
+  AcsLibraryInteraction.cpp)
 
-#target_link_libraries(
-  #acsd
-  #castorAcsDaemon
-  #castortapereactor
-  #castorcommon
-  #castorserver
- # castormessages
-  #zmq
-  #${STK_LIBRARIES})
+target_link_libraries(cta-acs
+  ctamediachanger 
+  ctacommon
+  ctareactor 
+  zmq
+  ${STK_LIBRARIES})
 
-#install (TARGETS acsd DESTINATION /usr/bin})
-#CastorInstallAdmManPage (acsd)
-#CastorInstallLogrotate (castor-acs-server)
-#CastorInstallSysconfigExample (acsd)
-#CastorInstallInitScript (acsd)
+set_target_properties (cta-acs PROPERTIES
+ COMPILE_FLAGS -I/usr/include/CDK
+ COMPILE_DEFINITIONS LINUX)
 
 ################################################################################
 # Rules to build and install castor-tape-acs-dismount
@@ -80,18 +53,17 @@ set (ACS_DISMOUNT_SRC_FILES
   AcsDismountCmd.cpp
   AcsDismountCmdLine.cpp
   AcsDismountCmdMain.cpp
-  CmdLineTool.cpp)
+  CmdLineTool.cpp
+  AcsDebugBuf.cpp)
 add_executable (cta-tape-acs-dismount ${ACS_DISMOUNT_SRC_FILES})
 set_target_properties (cta-tape-acs-dismount PROPERTIES
   COMPILE_FLAGS -I/usr/include/CDK
   COMPILE_DEFINITIONS LINUX)
 target_link_libraries (cta-tape-acs-dismount
-  ctamediachanger
   ctacommon
   ${STK_LIBRARIES})
 install (TARGETS cta-tape-acs-dismount DESTINATION /usr/bin)
 install (FILES cta-tape-acs-dismount.1cta DESTINATION /usr/share/man/man1)
-#CastorInstallExeManPage(cta-tape-acs-dismount)
 
 ################################################################################
 # Rules to build and install cta-tape-acs-mount
@@ -104,18 +76,17 @@ set (ACS_MOUNT_SRC_FILES
   AcsMountCmd.cpp
   AcsMountCmdLine.cpp
   AcsMountCmdMain.cpp
-  CmdLineTool.cpp)
+  CmdLineTool.cpp
+  AcsDebugBuf.cpp)
 add_executable (cta-tape-acs-mount ${ACS_MOUNT_SRC_FILES})
 set_target_properties (cta-tape-acs-mount PROPERTIES
   COMPILE_FLAGS -I/usr/include/CDK
   COMPILE_DEFINITIONS LINUX)
 target_link_libraries (cta-tape-acs-mount
-ctamediachanger
 ctacommon
 ${STK_LIBRARIES})
 install (TARGETS cta-tape-acs-mount DESTINATION /usr/bin)
 install (FILES cta-tape-acs-mount.1cta DESTINATION /usr/share/man/man1)
-#CastorInstallExeManPage(cta-tape-acs-mount)
 
 ################################################################################
 # Rules to build and install cta-tape-acs-queryvolume
@@ -129,18 +100,41 @@ set (ACS_QUERYVOLUME_SRC_FILES
   AcsQueryVolumeCmd.cpp
   AcsQueryVolumeCmdLine.cpp
   AcsQueryVolumeCmdMain.cpp
-  CmdLineTool.cpp)
+  CmdLineTool.cpp
+  AcsDebugBuf.cpp)
 
 add_executable (cta-tape-acs-queryvolume ${ACS_QUERYVOLUME_SRC_FILES})
 target_link_libraries (cta-tape-acs-queryvolume
-ctamediachanger
 ctacommon
 ${STK_LIBRARIES})
 set_target_properties (cta-tape-acs-queryvolume PROPERTIES
   COMPILE_FLAGS -I/usr/include/CDK
-  #COMPILE_FLAGS -I/usr/include/STK_INCLUDE_DIR
   COMPILE_DEFINITIONS LINUX)
 install (TARGETS cta-tape-acs-queryvolume
   DESTINATION /usr/bin)
 install (FILES cta-tape-acs-queryvolume.1cta DESTINATION /usr/share/man/man1)
-#CastorInstallExeManPage(cta-tape-acs-queryvolume)
+
+################################################################################
+# Rules to build and install cta-tape-acs-querydrive
+################################################################################
+message(STATUS "Adding cta-tape-acs-querydrive target")
+set (ACS_QUERYDRIVE_SRC_FILES
+  Acs.cpp
+  AcsCmd.cpp
+  AcsCmdLine.cpp
+  AcsImpl.cpp
+  AcsQueryDriveCmd.cpp
+  AcsQueryDriveCmdLine.cpp
+  AcsQueryDriveCmdMain.cpp
+  CmdLineTool.cpp
+  AcsDebugBuf.cpp)
+
+add_executable (cta-tape-acs-querydrive ${ACS_QUERYDRIVE_SRC_FILES})
+target_link_libraries (cta-tape-acs-querydrive
+ctacommon
+${STK_LIBRARIES})
+set_target_properties (cta-tape-acs-querydrive PROPERTIES
+  COMPILE_FLAGS -I/usr/include/CDK
+  COMPILE_DEFINITIONS LINUX)
+install (TARGETS cta-tape-acs-querydrive
+  DESTINATION /usr/bin)
diff --git a/mediachanger/acs/CmdLineTool.cpp b/mediachanger/acs/CmdLineTool.cpp
index 71ccd623f441e9180cae78eb44fbdc8b9e9ad363..7dcaa3c46983c5145dcbbfba1349b3660be4744b 100644
--- a/mediachanger/acs/CmdLineTool.cpp
+++ b/mediachanger/acs/CmdLineTool.cpp
@@ -21,7 +21,7 @@
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-cta::acs::CmdLineTool::CmdLineTool(std::istream &inStream,
+cta::mediachanger::acs::CmdLineTool::CmdLineTool(std::istream &inStream,
   std::ostream &outStream, std::ostream &errStream) throw():
   m_in(inStream), m_out(outStream), m_err(errStream), m_debugBuf(outStream),
   m_dbg(&m_debugBuf) {
@@ -30,12 +30,12 @@ cta::acs::CmdLineTool::CmdLineTool(std::istream &inStream,
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-cta::acs::CmdLineTool::~CmdLineTool() throw() {
+cta::mediachanger::acs::CmdLineTool::~CmdLineTool() throw() {
 }
 
 //------------------------------------------------------------------------------
 // bool2Str
 //------------------------------------------------------------------------------
-std::string cta::acs::CmdLineTool::bool2Str(const bool value) const throw() {
+std::string cta::mediachanger::acs::CmdLineTool::bool2Str(const bool value) const throw() {
   return value ? "TRUE" : "FALSE";
 }
diff --git a/mediachanger/acs/CmdLineTool.hpp b/mediachanger/acs/CmdLineTool.hpp
index 2137e16a7e4944f7ee02cf776980fac5bbd1f7d1..74e473b5fcb1aabda8de46fbe57e3e1b239324ca 100644
--- a/mediachanger/acs/CmdLineTool.hpp
+++ b/mediachanger/acs/CmdLineTool.hpp
@@ -18,13 +18,14 @@
 
 #pragma once
 
-#include "mediachanger/DebugBuf.hpp"
+#include "mediachanger/acs/AcsDebugBuf.hpp"
 
 #include <istream>
 #include <ostream>
 #include <string>
 
 namespace cta {
+namespace mediachanger {
 namespace acs {
 
 /**
@@ -69,7 +70,7 @@ protected:
    * Debug stream buffer that inserts a standard debug preamble before each
    * message-line written to it.
    */
-  cta::mediachanger::DebugBuf m_debugBuf;
+  cta::mediachanger::acs::DebugBuf m_debugBuf;
 
   /**
    * Stream used to write debug messages.
@@ -89,4 +90,5 @@ protected:
 }; // class CmdLineTool
 
 } // namespace acs
+} // namespace mediachanger
 } // namespace cta
diff --git a/mediachanger/acs/Constants.hpp b/mediachanger/acs/Constants.hpp
index f6c8056962420efe6502a9a23eab426b6fb0ca4e..b6c9cfb970840ca2d955a7afc8d9c677858cdfa2 100644
--- a/mediachanger/acs/Constants.hpp
+++ b/mediachanger/acs/Constants.hpp
@@ -19,7 +19,7 @@
 #pragma once
 
 namespace cta     {
-namespace acs        {
+//namespace acs        {
 
 /**
  * The default TCP/IP port on which the CASTOR ACS daemon listens for incoming Zmq
@@ -57,6 +57,6 @@ enum RequestState {
   ACS_REQUEST_FAILED,
   ACS_REQUEST_TO_DELETE};
 
-} // namespace acs
+//} // namespace acs
 } // namespace cta
 
diff --git a/mediachanger/acs/daemon/AcsDaemon.cpp b/mediachanger/acs/daemon/AcsDaemon.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4142f8e600a99a4ab94dc98a68748a73a7b14f56
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsDaemon.cpp
@@ -0,0 +1,374 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/Constants.hpp"
+#include "AcsDaemon.hpp"
+#include "AcsdConfiguration.hpp"
+#include "mediachanger/acs/daemon/AcsMessageHandler.hpp"
+#include "AcsPendingRequests.hpp"
+#include "mediachanger/acs/daemon/AcsdCmdLine.hpp"
+#include "common/exception/Errnum.hpp"
+#include "common/exception/BadAlloc.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/log.hpp"
+#include "common/log/SyslogLogger.hpp"
+#include "common/threading/Daemon.hpp"
+#include <memory>
+#include <signal.h>
+#include <unistd.h>
+
+#include <getopt.h>
+#include <iostream>
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+namespace cta { namespace mediachanger { namespace acs { namespace daemon {
+
+AcsDaemon::AcsDaemon(
+  const int argc,
+  char **const argv,
+  cta::log::Logger& log,
+  cta::mediachanger::reactor::ZMQReactor &reactor,
+  const AcsdConfiguration &config):
+  cta::server::Daemon(log),
+  m_argc(argc),
+  m_argv(argv),
+  m_log(log),  
+  m_reactor(reactor),
+  m_programName("acsd"),
+  m_hostName(getHostName()),  
+  m_zmqContext(NULL),
+  m_config(config),
+  m_acsPendingRequests(config,log) {
+}
+
+//------------------------------------------------------------------------------
+// getHostName
+//------------------------------------------------------------------------------
+
+std::string cta::mediachanger::acs::daemon::AcsDaemon::getHostName() const  {
+  char nameBuf[81];
+  if(gethostname(nameBuf, sizeof(nameBuf))) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to get host name: "<<ex.getMessage().str();
+    m_log(LOG_ERR, ex.getMessage().str());  
+    throw ex;
+  }
+
+  return nameBuf;
+}
+
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+  AcsDaemon::~AcsDaemon() throw() {  
+  m_reactor.clear();  
+  destroyZmqContext();
+  google::protobuf::ShutdownProtobufLibrary();
+}
+
+//------------------------------------------------------------------------------
+// destroyZmqContext
+//------------------------------------------------------------------------------
+
+  void AcsDaemon::destroyZmqContext() throw() {
+  if(NULL != m_zmqContext) {
+    if(zmq_term(m_zmqContext)) {
+      cta::exception::Exception ex;
+      std::list<log::Param> params = {log::Param("message", ex.getMessage().str())};
+      m_log(LOG_ERR, "Failed to destroy ZMQ context", params);
+    } else {
+      m_zmqContext = NULL;
+      m_log(LOG_INFO, "Successfully destroyed ZMQ context");
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int AcsDaemon::main() throw() {
+    
+  try {
+    exceptionThrowingMain(m_argc, m_argv);
+  } catch (cta::exception::Exception &ex) {
+    // Write the error to standard error
+    std::cerr << std::endl << "Aborting: " << ex.getMessage().str() << std::endl
+      << std::endl;
+    // Log the error
+    std::list<log::Param> params = {
+    log::Param("Message", ex.getMessage().str())};
+    m_log(LOG_ERR, "Aborting", params);
+
+    return 1;
+  }
+
+  return 0;
+}
+//------------------------------------------------------------------------------
+// exceptionThrowingMain
+//------------------------------------------------------------------------------
+
+void AcsDaemon::exceptionThrowingMain(
+  const int argc, char **const argv)  {
+  logStartOfDaemon(argc, argv);
+  AcsdCmdLine Commandline(argc,argv); //parse command line
+  setCommandLineHasBeenParsed(Commandline.foreground);
+  const std::string runAsStagerSuperuser = m_config.daemonUserName.value();
+  const std::string runAsStagerSupergroup = m_config.daemonGroupName.value();
+  daemonizeIfNotRunInForegroundAndSetUserAndGroup(runAsStagerSuperuser, runAsStagerSupergroup);
+  setDumpable();
+
+  blockSignals();
+  initZmqContext();
+  setUpReactor();  
+  mainEventLoop();
+
+}
+
+//------------------------------------------------------------------------------
+// logStartOfDaemon
+//------------------------------------------------------------------------------
+
+  void AcsDaemon::logStartOfDaemon(
+  const int argc, const char *const *const argv) throw() {
+  const std::string concatenatedArgs = argvToString(argc, argv);
+  std::ostringstream msg;
+  msg << m_programName << " started";
+
+  std::list<log::Param> params = {
+    log::Param("argv", concatenatedArgs)};
+  m_log(LOG_INFO, msg.str(), params);
+}
+
+//------------------------------------------------------------------------------
+// argvToString
+//------------------------------------------------------------------------------
+
+  std::string AcsDaemon::argvToString(
+  const int argc, const char *const *const argv) throw() {
+  std::string str;
+
+  for(int i=0; i < argc; i++) {
+    if(i != 0) {
+      str += " ";
+    }
+
+    str += argv[i];
+  }
+  return str;
+}
+
+//------------------------------------------------------------------------------
+// setDumpable
+//------------------------------------------------------------------------------
+void AcsDaemon::setDumpable() {
+  cta::utils::setDumpableProcessAttribute(true);
+  const bool dumpable = cta::utils::getDumpableProcessAttribute();
+  std::list<log::Param> params = {
+    log::Param("dumpable", dumpable ? "true" : "false")};
+  m_log(LOG_INFO, "Got dumpable attribute of process", params);
+  if(!dumpable) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to set dumpable attribute of process to true";
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// blockSignals
+//------------------------------------------------------------------------------
+void AcsDaemon::blockSignals() const {
+  sigset_t sigs;
+  sigemptyset(&sigs);
+  // The signals that should not asynchronously disturb the daemon
+  sigaddset(&sigs, SIGHUP);
+  sigaddset(&sigs, SIGINT);
+  sigaddset(&sigs, SIGQUIT);
+  sigaddset(&sigs, SIGPIPE);
+  sigaddset(&sigs, SIGTERM);
+  sigaddset(&sigs, SIGUSR1);
+  sigaddset(&sigs, SIGUSR2);
+  sigaddset(&sigs, SIGCHLD);
+  sigaddset(&sigs, SIGTSTP);
+  sigaddset(&sigs, SIGTTIN);
+  sigaddset(&sigs, SIGTTOU);
+  sigaddset(&sigs, SIGPOLL);
+  sigaddset(&sigs, SIGURG);
+  sigaddset(&sigs, SIGVTALRM);
+  cta::exception::Errnum::throwOnNonZero(
+    sigprocmask(SIG_BLOCK, &sigs, NULL),
+    "Failed to block signals: sigprocmask() failed");
+}
+
+//------------------------------------------------------------------------------
+// initZmqContext
+//------------------------------------------------------------------------------
+
+  void AcsDaemon::initZmqContext() {
+  const int sizeOfIOThreadPoolForZMQ = 1;
+  m_zmqContext = zmq_init(sizeOfIOThreadPoolForZMQ);
+  if(NULL == m_zmqContext) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to instantiate ZMQ context: " << ex.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// setUpReactor
+//------------------------------------------------------------------------------
+void AcsDaemon::setUpReactor() {
+  createAndRegisterAcsMessageHandler();
+}
+
+//------------------------------------------------------------------------------
+// createAndRegisterAcsMessageHandler
+//------------------------------------------------------------------------------
+void AcsDaemon::createAndRegisterAcsMessageHandler()  {
+  try {
+    std::unique_ptr<AcsMessageHandler> handler;
+    try {
+      handler.reset(new AcsMessageHandler(m_log, m_reactor, m_zmqContext, m_hostName,
+        m_config,  m_acsPendingRequests));   //create event handler for communicating with the acs daemon
+    } catch(std::bad_alloc &ba) {
+      cta::exception::BadAlloc ex;
+      ex.getMessage() <<
+        "Failed to create event handler for communicating with "
+        "the CTA ACS daemon: " << ba.what();
+      throw ex;
+    }
+    m_reactor.registerHandler(handler.get());
+    handler.release();
+  } catch(cta::exception::Exception &ne) {
+    cta::exception::Exception ex;
+    ex.getMessage() <<
+      "Failed to create and register AcsMessageHandler: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// mainEventLoop
+//------------------------------------------------------------------------------
+void AcsDaemon::mainEventLoop() {
+  while(handleEvents()) {
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleEvents
+//------------------------------------------------------------------------------
+bool AcsDaemon::handleEvents() { 
+  try {
+    const int timeout = 100; // 100 milliseconds
+    m_reactor.handleEvents(timeout);
+  } catch(cta::exception::Exception &ex) {
+    // Log exception and continue
+    std::list<log::Param> params = {
+      log::Param("message", ex.getMessage().str()),
+      log::Param("backtrace", ex.backtrace())
+    };
+    m_log(LOG_ERR,
+      "Unexpected cta exception thrown when handling an I/O event", params);
+  } catch(std::exception &se) {
+    // Log exception and continue
+    std::list<log::Param> params = {log::Param("message", se.what())};
+    m_log(LOG_ERR, "Unexpected exception thrown when handling an I/O event",
+      params);
+  } catch(...) {
+    // Log exception and continue
+    m_log(LOG_ERR,
+      "Unexpected and unknown exception thrown when handling an I/O event");
+  }
+  
+  try {
+    handlePendingRequests();
+  } catch(cta::exception::Exception &ex) {
+    // Log exception and continue
+    std::list<log::Param> params = {
+      log::Param("message", ex.getMessage().str()),
+      log::Param("backtrace", ex.backtrace())
+    };
+    m_log(LOG_ERR,
+      "Unexpected cta exception thrown when handling pending requests", 
+      params);
+  } catch(std::exception &se) {
+    // Log exception and continue
+    std::list<log::Param> params = {log::Param("message", se.what())};
+    m_log(LOG_ERR,
+      "Unexpected exception thrown when handling pending requests", params);
+  } catch(...) {
+    // Log exception and continue
+    m_log(LOG_ERR,
+      "Unexpected and unknown exception thrown when handling pending requests");
+  }
+  
+  return handlePendingSignals();
+}
+
+//------------------------------------------------------------------------------
+// handlePendingRequests
+//------------------------------------------------------------------------------
+void AcsDaemon::handlePendingRequests() {
+  m_acsPendingRequests.tick(); 
+  m_acsPendingRequests.handleCompletedRequests();
+  m_acsPendingRequests.handleFailedRequests();
+  m_acsPendingRequests.handleToDeleteRequests();
+}
+
+//------------------------------------------------------------------------------
+// handlePendingSignals
+//------------------------------------------------------------------------------
+
+bool AcsDaemon::handlePendingSignals() throw() {
+  bool continueMainEventLoop = true;
+  int sig = 0;
+  sigset_t allSignals;
+  siginfo_t sigInfo;
+  sigfillset(&allSignals);
+  struct timespec immediateTimeout = {0, 0};
+
+  // While there is a pending signal to be handled
+  while (0 < (sig = sigtimedwait(&allSignals, &sigInfo, &immediateTimeout))) {
+    switch(sig) {
+    case SIGINT: // Signal number 2
+      m_log(LOG_INFO, "Stopping gracefully because SIGINT was received");
+      continueMainEventLoop = false;
+      break;
+    case SIGTERM: // Signal number 15
+      m_log(LOG_INFO, "Stopping gracefully because SIGTERM was received");
+      continueMainEventLoop = false;
+      break;
+    default:
+      {
+        std::list<log::Param> params = {log::Param("signal", sig)};
+        m_log(LOG_INFO, "Ignoring signal", params);
+      }
+      break;
+    }
+  }
+       
+  return continueMainEventLoop;
+}
+
+
+}}}}
diff --git a/mediachanger/acs/daemon/AcsDaemon.hpp b/mediachanger/acs/daemon/AcsDaemon.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6f61f4e1ff9531441b9000f665307c0bfbbcf5f
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsDaemon.hpp
@@ -0,0 +1,213 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include "common/threading/Daemon.hpp"
+#include "mediachanger/reactor/ZMQReactor.hpp"
+#include "AcsdConfiguration.hpp"
+#include "AcsPendingRequests.hpp"
+#include "common/Constants.hpp"
+#include "common/log/SyslogLogger.hpp"
+#include "AcsdCmdLine.hpp"
+#include "common/processCap/ProcessCap.hpp"
+#include <signal.h>
+
+#include <getopt.h>
+
+namespace cta {   
+namespace mediachanger {   
+namespace acs {  
+namespace daemon {
+
+/**
+ * CTA ACS daemon responsible for mounting and dismounting tapes for ACS.
+ */
+class AcsDaemon : public server::Daemon {
+
+public:
+  /**
+   * Constructor.
+   *
+   * @param argc The argc of main().
+   * @param argv The argv of main().
+   * @param stdOut Stream representing standard out.
+   * @param stdErr Stream representing standard error.
+   * @param reactor The reactor responsible for dispatching the I/O requests to
+   * the CTA ACS daemon.
+   * @param config The CTA configuration parameters used by the CTA ACS
+   * daemon.
+   */
+  AcsDaemon(
+    const int argc,
+    char **const argv,
+    log::Logger& log,
+    cta::mediachanger::reactor::ZMQReactor &reactor,
+    const AcsdConfiguration &config);
+
+  /**
+   * Destructor.
+   */
+  ~AcsDaemon() throw();
+
+  /**
+   * The main entry function of the daemon.
+   *
+   * @return The return code of the process.
+   */
+  int main() throw();
+  
+protected:
+
+  /**
+   * Returns the name of the host on which the daemon is running.
+   */
+  std::string getHostName() const;
+
+  /**
+   * Exception throwing main() function.
+   *
+   * @param argc The number of command-line arguments.
+   * @param argv The array of command-line arguments.
+   */
+  void exceptionThrowingMain(const int argc, char **const argv);
+
+  /**
+   * Logs the start of the daemon.
+   */
+  void logStartOfDaemon(const int argc, const char *const *const argv) throw();
+
+  /**
+   * Creates a string that contains the specified command-line arguments
+   * separated by single spaces.
+   *
+   * @param argc The number of command-line arguments.
+   * @param argv The array of command-line arguments.
+   */
+  std::string argvToString(const int argc, const char *const *const argv)
+    throw();
+
+  /**
+   * Idempotent method that destroys the ZMQ context.
+   */
+  void destroyZmqContext() throw();
+
+  /**
+   * Sets the dumpable attribute of the current process to true.
+   */
+  void setDumpable();
+
+  /**
+   * Blocks the signals that should not asynchronously disturb the daemon.
+   */
+  void blockSignals() const;
+  
+  /**
+   * Initialises the ZMQ context.
+   */
+  void initZmqContext();
+  /**
+   * Sets up the reactor.
+   */
+  void setUpReactor();
+ 
+  /**
+   * Creates the handler to handle messages for the acs Zmq requests.
+   */
+  void createAndRegisterAcsMessageHandler();
+  
+  /**
+   * The main event loop of the daemon.
+   */
+  void mainEventLoop();
+
+  /**
+   * Handles any pending events.
+   *
+   * @return True if the main event loop should continue, else false.
+   */
+  bool handleEvents();
+
+  /**
+   * Handles any pending signals.
+   *
+   * @return True if the main event loop should continue, else false.
+   */
+  bool handlePendingSignals() throw();
+  
+  /**
+   * Handles any pending Acs requests.
+   *
+   */
+  void handlePendingRequests();
+
+  /**
+   * The argc of main().
+   */
+  const int m_argc;
+
+  /**
+   * The argv of main().
+   */
+  char **const m_argv;
+
+  log::Logger &m_log;
+  /**
+   * The reactor responsible for dispatching the file-descriptor event-handlers
+   * of the CTA ACS daemon.
+   */
+  cta::mediachanger::reactor::ZMQReactor &m_reactor;
+
+  /**
+   * The program name of the daemon.
+   */
+  const std::string m_programName;
+
+  /**
+   * The name of the host on which the daemon is running. 
+   */
+  const std::string m_hostName;
+
+  /**
+   * The ZMQ context.
+   */
+  void *m_zmqContext;
+  
+  /**
+   * The CTA configuration parameters used by the CTA ACS daemon.
+   */
+  const AcsdConfiguration m_config;
+  
+  /**
+   * The object to handle requests to the CTA ACS daemon.
+   */
+  AcsPendingRequests m_acsPendingRequests;
+
+private:
+ 
+  /**
+   * Flag indicating whether the server should run in foreground or background
+   * mode.
+   */
+  bool m_foreground;
+
+}; // class AcsDaemon
+
+} // namespace daemon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsDaemonMain.cpp b/mediachanger/acs/daemon/AcsDaemonMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0f603570ca84768e3a8543b8bd93fb34985dc24f
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsDaemonMain.cpp
@@ -0,0 +1,98 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/log/log.hpp"
+#include "common/log/SyslogLogger.hpp"
+#include "mediachanger/acs/Constants.hpp"
+#include "AcsDaemon.hpp"
+#include "AcsdCmdLine.hpp"
+#include "mediachanger/reactor/ZMQReactor.hpp"
+#include "AcsdConfiguration.hpp"
+#include "common/utils/utils.hpp"
+#include "common/exception/Exception.hpp"
+#include <iostream>
+
+
+//------------------------------------------------------------------------------
+// exceptionThrowingMain
+//
+// The main() function delegates the bulk of its implementation to this
+// exception throwing version.
+//
+// @param argc The number of command-line arguments.
+// @param argv The command-line arguments.
+//------------------------------------------------------------------------------
+static int exceptionThrowingMain(cta::log::Logger &log,const int argc,char **const argv);
+
+//------------------------------------------------------------------------------
+// main
+//------------------------------------------------------------------------------
+int main(const int argc, char **const argv) {
+  using namespace cta;
+
+    const std::string shortHostName = utils::getShortHostname();
+    cta::log::SyslogLogger logger(shortHostName, "cta-acsd", log::DEBUG);
+  try {
+    logger(LOG_INFO, "started ACSD in CTA");
+  } catch(cta::exception::Exception &ex) {
+    std::cerr <<
+      "Failed to instantiate object representing CTA logging system: " <<
+      ex.getMessage().str() << std::endl;
+    return 1;
+  } 
+  int programRc = 1; 
+  try {
+    programRc = exceptionThrowingMain(logger, argc, argv) ;
+  } catch(cta::exception::Exception &ex) {
+    std::list<log::Param> params = {
+      log::Param("message", ex.getMessage().str())};
+    logger(LOG_ERR, "Caught an unexpected CTA exception", params);
+  } catch(std::exception &se) {
+    std::list<log::Param> params = {log::Param("what", se.what())};
+    logger(LOG_ERR, "Caught an unexpected standard exception", params);
+  } catch(...) {
+    logger(LOG_ERR, "Caught an unexpected and unknown exception");
+  }
+
+  return programRc;
+}
+
+//------------------------------------------------------------------------------
+// exceptionThrowingMain
+//------------------------------------------------------------------------------
+static int exceptionThrowingMain(cta::log::Logger &log, const int argc, char **const argv) {
+  
+
+ cta::mediachanger::reactor::ZMQReactor reactor(log);
+
+ const cta::mediachanger::acs::daemon::AcsdConfiguration config = cta::mediachanger::acs::daemon::AcsdConfiguration::createFromCtaConf("/etc/cta/cta-acsd.conf",log);
+
+ 
+  // Create the main acsd object
+  cta::mediachanger::acs::daemon::AcsDaemon daemon(
+    argc,
+    argv,
+    log,
+    reactor,
+    config);
+
+  // Run the acsd daemon
+  return daemon.main();
+
+return 0;
+}
diff --git a/mediachanger/acs/daemon/AcsDismountTape.cpp b/mediachanger/acs/daemon/AcsDismountTape.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99354c573a7e34ec1df86043eead8386c7d555b6
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsDismountTape.cpp
@@ -0,0 +1,142 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#include "AcsDismountTape.hpp"
+#include "common/exception/DismountFailed.hpp"
+#include "common/log/log.hpp"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsDismountTape::AcsDismountTape(
+  const std::string &vid,
+  const uint32_t acs,
+  const uint32_t lsm,
+  const uint32_t panel,
+  const uint32_t drive,
+  Acs &acsWrapper,
+  log::Logger& log,
+  const AcsdConfiguration &ctaConf):
+  cta::mediachanger::acs::AcsLibraryInteraction(acsWrapper, log),
+  m_volId(acsWrapper.str2Volid(vid)),
+  m_driveId(acsWrapper.alpd2DriveId(acs,lsm,panel,drive)), 
+  m_acsWrapper(acsWrapper),
+  m_log(log),
+  m_ctaConf(ctaConf) {
+}
+
+//------------------------------------------------------------------------------
+// execute
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsDismountTape::execute() const {
+  syncDismount();
+}
+
+//------------------------------------------------------------------------------
+// asyncExecute
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsDismountTape::asyncExecute(const SEQ_NO seqNo) const {
+  asyncDismount(seqNo);
+}
+
+
+//------------------------------------------------------------------------------
+// syncDismount
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsDismountTape::syncDismount() const {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendDismountRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf,
+     m_ctaConf.QueryInterval.value(),
+     m_ctaConf.CmdTimeout.value());
+    processDismountResponse(buf);
+  } catch(cta::exception::Exception &ex) {
+    cta::exception::DismountFailed df;
+    df.getMessage() << "Failed to dismount volume " <<
+      m_volId.external_label << ": " << ex.getMessage().str();     
+    throw df;
+  }
+}
+
+//------------------------------------------------------------------------------
+// asyncDismount
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsDismountTape::asyncDismount(const SEQ_NO seqNo) const
+  {
+  try {
+    sendDismountRequest(seqNo);    
+  } catch(cta::exception::Exception &ex) {
+    cta::exception::DismountFailed df;
+    df.getMessage() << "Failed to send dismount request to ACS " <<
+      m_volId.external_label << ": " << ex.getMessage().str();     
+    throw df;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendDismountRequest
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsDismountTape::sendDismountRequest(
+  const SEQ_NO seqNumber) const {
+  const LOCKID lockId = 0; // No lock
+  const BOOLEAN force = FALSE; 
+  
+  std::stringstream dbgMsg;
+  dbgMsg << "Calling Acs::dismount() with seqNumber=" << seqNumber;
+  m_log(LOG_DEBUG, dbgMsg.str());
+  const STATUS s = m_acsWrapper.dismount(seqNumber, lockId, m_volId,
+    m_driveId, force);
+  
+  dbgMsg.str("");
+  dbgMsg << "Acs::dismount() for seqNumber=" << seqNumber << " returned " <<
+    acs_status(s);           
+  m_log(LOG_DEBUG,dbgMsg.str());
+  if(STATUS_SUCCESS != s) {
+    cta::exception::DismountFailed ex;
+    ex.getMessage() << "Failed to send request to dismount volume " <<
+      m_volId.external_label << " from drive " <<
+      m_acsWrapper.driveId2Str(m_driveId) << ": force=" <<
+      (force ? "TRUE" : "FALSE") << ": " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// processDismountResponse
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsDismountTape::processDismountResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const
+  {
+  const ACS_DISMOUNT_RESPONSE *const msg = (ACS_DISMOUNT_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->dismount_status) {
+    cta::exception::DismountFailed ex;
+    ex.getMessage() << "Status of dismount response is not success: " <<
+      acs_status(msg->dismount_status);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsDismountTape::~AcsDismountTape() throw() {  
+}
diff --git a/mediachanger/acs/daemon/AcsDismountTape.hpp b/mediachanger/acs/daemon/AcsDismountTape.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bca01954a195dbf19b34c3a3d25cebaa9b4f17d3
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsDismountTape.hpp
@@ -0,0 +1,130 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/acs/Acs.hpp"
+#include "AcsdConfiguration.hpp"
+#include "mediachanger/acs/AcsLibraryInteraction.hpp"
+
+namespace cta     {
+namespace mediachanger        {
+namespace acs	{
+namespace daemon	{
+
+/**
+ * Class responsible for dismounting tapes through ACS API.
+ */
+class AcsDismountTape: public cta::mediachanger::acs::AcsLibraryInteraction {
+
+public:
+
+  /**
+   * Constructor.
+   */
+  AcsDismountTape(
+    const std::string &vid,
+    const uint32_t acs,
+    const uint32_t lsm,
+    const uint32_t panel,
+    const uint32_t drive,
+    cta::mediachanger::acs::Acs &acsWrapper,
+    cta::log::Logger& log,
+    const mediachanger::acs::daemon::AcsdConfiguration &ctaConf);
+
+  /**
+   * Destructor.
+   */
+  ~AcsDismountTape() throw();
+
+  /**
+   * Execute dismount request through ACS API.
+   */
+  void execute() const;
+  
+  /**
+   * Execute asynchronous dismount request through ACS API.
+   * 
+   * @param The value of sequence number for ACS API.
+   */
+  void asyncExecute(const SEQ_NO seqNo) const;
+  
+protected:
+
+  /**
+   * Dismounts the tape with the specified m_volId from the drive with the
+   * specified m_driveId.
+   *
+   * This method does not return until the dismount has either succeeded, failed
+   * or the specified timeout has been reached.
+   */
+  void syncDismount() const;
+  
+  
+  /**
+   * Dismounts the tape with the specified m_volId from the drive with the
+   * specified m_driveId.
+   * This method sends a dismount request to ACSLS and returns.
+   * 
+   * @param The value of sequence number for ACS API.
+   */
+  void asyncDismount(const SEQ_NO seqNo) const;
+  
+  /**
+   * Sends the dismount request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendDismountRequest(const SEQ_NO seqNumber) const;
+ 
+  /**
+   * Throws cta::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)]) const;
+  
+  /**
+   * VOLID
+   */  
+  VOLID m_volId;
+  
+  /**
+   * DRIVEID
+   */  
+  DRIVEID m_driveId;  
+  
+  /**
+   * Object providing c wrapper for ACS commands.
+   */
+  Acs &m_acsWrapper;
+
+  log::Logger &m_log;
+  /**
+   * The configuration parameters for the CTA ACS daemon.
+   */
+  const mediachanger::acs::daemon::AcsdConfiguration m_ctaConf;
+
+}; // class AcsDismountTape
+
+} // namespace daemon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsForceDismountTape.cpp b/mediachanger/acs/daemon/AcsForceDismountTape.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..439b3b5e1fff332293acd06016d0760b73e0820f
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsForceDismountTape.cpp
@@ -0,0 +1,142 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#include "AcsForceDismountTape.hpp"
+#include "common/exception/ForceDismountFailed.hpp"
+#include "common/log/log.hpp"
+#include "common/log/SyslogLogger.hpp"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsForceDismountTape::AcsForceDismountTape(
+  const std::string &vid,
+  const uint32_t acs,
+  const uint32_t lsm,
+  const uint32_t panel,
+  const uint32_t drive,
+  cta::mediachanger::acs::Acs &acsWrapper,
+  cta::log::Logger& log,
+  const AcsdConfiguration &ctaConf):
+  AcsLibraryInteraction(acsWrapper, log),
+  m_volId(acsWrapper.str2Volid(vid)),
+  m_driveId(acsWrapper.alpd2DriveId(acs,lsm,panel,drive)), 
+  m_acsWrapper(acsWrapper),
+  m_log(log),
+  m_ctaConf(ctaConf) {
+}
+
+//------------------------------------------------------------------------------
+// execute
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsForceDismountTape::execute() const {
+  syncForceDismount();
+}
+
+//------------------------------------------------------------------------------
+// asyncExecute
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsForceDismountTape::asyncExecute(const SEQ_NO seqNo) const {
+  asyncForceDismount(seqNo);
+}
+
+
+//------------------------------------------------------------------------------
+// syncForceDismount
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsForceDismountTape::syncForceDismount() const {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendForceDismountRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf,
+      m_ctaConf.QueryInterval.value(),
+      m_ctaConf.CmdTimeout.value());
+    processForceDismountResponse(buf);
+  } catch(cta::exception::Exception &ex) {
+    cta::exception::ForceDismountFailed df;
+    df.getMessage() << "Failed to force dismount volume " <<
+      m_volId.external_label << ": " << ex.getMessage().str();     
+    throw df;
+  }
+}
+
+//------------------------------------------------------------------------------
+// asyncDismount
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsForceDismountTape::asyncForceDismount(const SEQ_NO seqNo)
+  const {
+  try {
+    sendForceDismountRequest(seqNo);    
+  } catch(cta::exception::Exception &ex) {
+    cta::exception::ForceDismountFailed df;
+    df.getMessage() << "Failed to send dismount request to ACS " <<
+      m_volId.external_label << ": " << ex.getMessage().str();     
+    throw df;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendDismountRequest
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsForceDismountTape::sendForceDismountRequest(
+  const SEQ_NO seqNumber) const {
+  const LOCKID lockId = 0; // No lock
+  const BOOLEAN force = TRUE; 
+  
+  std::stringstream dbgMsg;
+  dbgMsg << "Calling Acs::dismount() with seqNumber=" << seqNumber;
+  m_log(LOG_DEBUG, dbgMsg.str());
+  const STATUS s = m_acsWrapper.dismount(seqNumber, lockId, m_volId,
+    m_driveId, force);
+  
+  dbgMsg.str("");
+  dbgMsg << "Acs::dismount() for seqNumber=" << seqNumber << " returned " <<
+    acs_status(s);           
+  m_log(LOG_DEBUG,dbgMsg.str());
+  if(STATUS_SUCCESS != s) {
+    cta::exception::ForceDismountFailed ex;
+    ex.getMessage() << "Failed to send request to force dismount volume " <<
+      m_volId.external_label << " from drive " <<
+      m_acsWrapper.driveId2Str(m_driveId) << ": force=" <<
+      (force ? "TRUE" : "FALSE") << ": " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// processForceDismountResponse
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsForceDismountTape::processForceDismountResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const {
+  const ACS_DISMOUNT_RESPONSE *const msg = (ACS_DISMOUNT_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->dismount_status) {
+    cta::exception::ForceDismountFailed ex;
+    ex.getMessage() << "Status of force dismount response is not success: " <<
+      acs_status(msg->dismount_status);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsForceDismountTape::~AcsForceDismountTape() throw() {  
+}
diff --git a/mediachanger/acs/daemon/AcsForceDismountTape.hpp b/mediachanger/acs/daemon/AcsForceDismountTape.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b92ec273c12976099b929bfedad869b24d1c0db
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsForceDismountTape.hpp
@@ -0,0 +1,133 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/acs/Acs.hpp"
+#include "mediachanger/acs/daemon/AcsdConfiguration.hpp"
+#include "mediachanger/acs/AcsLibraryInteraction.hpp"
+#include "common/log/log.hpp"
+#include "common/log/SyslogLogger.hpp"
+
+namespace cta        {
+namespace mediachanger     {
+namespace acs        {
+namespace daemon       {
+
+/**
+ * Class responsible for dismounting tapes through ACS API.
+ */
+class AcsForceDismountTape: public cta::mediachanger::acs::AcsLibraryInteraction {
+
+public:
+
+  /**
+   * Constructor.
+   */
+  AcsForceDismountTape(
+    const std::string &vid,
+    const uint32_t acs,
+    const uint32_t lsm,
+    const uint32_t panel,
+    const uint32_t drive,
+    cta::mediachanger::acs::Acs &acsWrapper,
+    log::Logger& log,
+    const AcsdConfiguration &ctaConf);
+
+  /**
+   * Destructor.
+   */
+  ~AcsForceDismountTape() throw();
+
+  /**
+   * Execute force dismount request through ACS API.
+   */
+  void execute() const;
+  
+  /**
+   * Execute asynchronous force dismount request through ACS API.
+   * 
+   * @param The value of sequence number for ACS API.
+   */
+  void asyncExecute(const SEQ_NO seqNo) const;
+  
+protected:
+
+  /**
+   * Force dismounts the tape with the specified m_volId from the drive with the
+   * specified m_driveId.
+   *
+   * This method does not return until the dismount has either succeeded, failed
+   * or the specified timeout has been reached.
+   */
+  void syncForceDismount() const;
+  
+  
+  /**
+   * Force dismounts the tape with the specified m_volId from the drive with the
+   * specified m_driveId.
+   * This method sends a dismount request to ACSLS and returns.
+   * 
+   * @param The value of sequence number for ACS API.
+   */
+  void asyncForceDismount(const SEQ_NO seqNo) const;
+  
+  /**
+   * Sends the force dismount request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendForceDismountRequest(const SEQ_NO seqNumber) const;
+ 
+  /**
+   * Throws cta::exception::DismountFailed if the mount was not
+   * successful.
+   *
+   * @param buf The mount-response message.
+   */
+  void processForceDismountResponse(
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const;
+  
+  /**
+   * VOLID
+   */  
+  VOLID m_volId;
+  
+  /**
+   * DRIVEID
+   */  
+  DRIVEID m_driveId;  
+  
+  /**
+   * Object providing c wrapper for ACS commands.
+   */
+  Acs &m_acsWrapper;
+  
+  log::Logger &m_log;
+
+  /**
+   * The configuration parameters for the CTA ACS daemon.
+   */
+  const AcsdConfiguration m_ctaConf;
+
+}; // class AcsForceDismountTape
+
+} // namespace daemon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsMessageHandler.cpp b/mediachanger/acs/daemon/AcsMessageHandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4388a97ff92451e51b1194810d29a510b96b1edb
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsMessageHandler.cpp
@@ -0,0 +1,444 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/Constants.hpp"
+#include "mediachanger/acs/daemon/Constants.hpp"
+#include "AcsMessageHandler.hpp"
+#include "AcsDismountTape.hpp"
+#include "AcsForceDismountTape.hpp"
+#include "AcsMountTapeReadOnly.hpp"
+#include "AcsMountTapeReadWrite.hpp"
+#include "mediachanger/acs/Acs.hpp"
+#include "mediachanger/acs/AcsImpl.hpp"
+#include "common/log/log.hpp"
+#include "common/log/SyslogLogger.hpp"
+#include "mediachanger/messages.hpp"
+#include "mediachanger/ReturnValue.pb.h"
+#include "mediachanger/AcsMountTapeReadOnly.pb.h"
+#include "mediachanger/AcsMountTapeReadWrite.pb.h"
+#include "mediachanger/AcsDismountTape.pb.h"
+#include "mediachanger/AcsForceDismountTape.pb.h"
+#include "mediachanger/Exception.pb.h"
+#include "mediachanger/reactor/ZMQPollEventHandler.hpp"
+#include "mediachanger/reactor/ZMQReactor.hpp"
+#include "mediachanger/ZmqSocket.hpp"
+#include "errno.h"
+
+#include <iostream>
+#include <unistd.h>
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsMessageHandler::AcsMessageHandler(
+  cta::log::Logger &log,
+  cta::mediachanger::reactor::ZMQReactor &reactor,
+  void *const zmqContext,
+  const std::string &hostName,
+  const AcsdConfiguration &ctaConf,
+  AcsPendingRequests &acsPendingRequests):
+  m_log(log),
+  m_reactor(reactor),
+  m_socket(zmqContext, ZMQ_ROUTER),
+  m_hostName(hostName),
+  m_ctaConf(ctaConf),
+  m_acsPendingRequests(acsPendingRequests) { 
+
+  std::ostringstream endpoint;
+  endpoint << "tcp://127.0.0.1:" << m_ctaConf.port.value();
+  
+  try {
+    m_socket.bind(endpoint.str().c_str());
+    std::list<log::Param> params = {log::Param("endpoint", endpoint.str())};
+    m_log(LOG_INFO, "Bound the ZMQ socket of the AcsMessageHandler",
+      params);
+  } catch(cta::exception::Exception &ne){
+    cta::exception::Exception ex;
+    ex.getMessage() <<
+      "Failed to bind the ZMQ socket of the AcsMessageHandler"
+      ": endpoint=" << endpoint.str() << ": " << ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsMessageHandler::~AcsMessageHandler()
+  throw() {
+}
+
+//------------------------------------------------------------------------------
+// getName
+//------------------------------------------------------------------------------
+std::string cta::mediachanger::acs::daemon::AcsMessageHandler::getName()
+  const throw() {
+  return "AcsMessageHandler";
+}
+
+//------------------------------------------------------------------------------
+// fillPollFd
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMessageHandler::fillPollFd(
+  zmq_pollitem_t &fd) throw() {
+  fd.events = ZMQ_POLLIN;
+  fd.revents = 0;
+  fd.socket = m_socket.getZmqSocket();
+  fd.fd = -1;
+}
+
+//------------------------------------------------------------------------------
+// handleEvent
+//------------------------------------------------------------------------------
+bool cta::mediachanger::acs::daemon::AcsMessageHandler::handleEvent(
+  const zmq_pollitem_t &fd) throw() {
+  // Try to receive a request, simply giving up if an exception is raised
+  cta::mediachanger::Frame rqst;
+
+  //for handling zeroMQ's router socket type specific elements 
+  //ie first frame = identity of the sender
+  //   second one  =  empty
+  //   third and following = actual data frames
+ 
+  //The ZmqMsg address data can be dump as string and used as key for storing 
+  //the identity (for clients who need a late answer)
+  cta::mediachanger::ZmqMsg address;
+  cta::mediachanger::ZmqMsg empty;
+  try {
+    checkSocket(fd);
+    m_socket.recv(address);
+    m_socket.recv(empty);
+    rqst = recvFrame(m_socket);
+  } catch(cta::exception::Exception &ex) {
+    std::list<log::Param> params = {log::Param("message", ex.getMessage().str())};
+    m_log(LOG_ERR, "AcsMessageHandler failed to handle event", params);
+    return false; // Give up and stay registered with the reactor
+  }
+  std::list<log::Param> params = {
+      log::Param("sender identity", 
+              utils::hexDump(address.getData(),address.size()))
+     };
+  m_log(LOG_DEBUG, "handling event in AcsMessageHandler", params);
+ 
+  // From this point on any exception thrown should be converted into an
+  // Exception message and sent back to the client 
+  cta::mediachanger::Frame reply;
+  
+  try {
+    // if there are any problems we need to send the replay to the client.
+    reply = dispatchMsgHandler(rqst);
+  } catch(cta::exception::Exception &ex) {
+    reply = createExceptionFrame(ECANCELED, ex.getMessage().str()); 
+    m_log(LOG_ERR, ex.getMessage().str());
+  } catch(std::exception &se) {
+    reply = createExceptionFrame(ECANCELED, se.what());
+    m_log(LOG_ERR, se.what());
+  } catch(...) {
+    reply = createExceptionFrame(ECANCELED, "Caught an unknown exception"); 
+    m_log(LOG_ERR, "Caught an unknown exception");
+  }
+
+  // Send the reply to the client
+  try {
+    //we need to prepend our frames the same way we received them
+    // ie identity + empty frames 
+    m_socket.send(address,ZMQ_SNDMORE);
+    m_socket.send(empty,ZMQ_SNDMORE);
+
+    cta::mediachanger::sendFrame(m_socket, reply);
+  } catch(cta::exception::Exception &ex) {
+    std::list<log::Param> params = {log::Param("message", ex.getMessage().str())};
+    m_log(LOG_ERR, "AcsMessageHandler failed to send reply to client", params);
+  }
+
+  return false; // Stay registered with the reactor
+}
+
+//------------------------------------------------------------------------------
+// checkSocket
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMessageHandler::checkSocket(
+  const zmq_pollitem_t &fd) const{
+  void* underlyingSocket = m_socket.getZmqSocket();
+  if(fd.socket != underlyingSocket){
+    cta::exception::Exception ex;
+    ex.getMessage() << "AcsMessageHandler passed wrong poll item";
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// dispatchMsgHandler
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsMessageHandler::
+  dispatchMsgHandler(const cta::mediachanger::Frame &rqst) {
+  m_log(LOG_DEBUG, "AcsMessageHandler dispatching message handler");
+  
+  const cta::mediachanger::acs::daemon::MsgType msgType = (cta::mediachanger::acs::daemon::MsgType)rqst.header.msgtype();
+  switch(msgType) {
+  case cta::mediachanger::MSG_TYPE_ACSMOUNTTAPEREADONLY:
+    return handleAcsMountTapeReadOnly(rqst);
+      
+  case cta::mediachanger::MSG_TYPE_ACSMOUNTTAPEREADWRITE:
+    return handleAcsMountTapeReadWrite(rqst);  
+
+  case cta::mediachanger::MSG_TYPE_ACSDISMOUNTTAPE:
+    return handleAcsDismountTape(rqst);
+
+  case cta::mediachanger::MSG_TYPE_ACSFORCEDISMOUNTTAPE:
+    return handleAcsForceDismountTape(rqst);
+
+  default:
+    {
+      const std::string msgTypeStr = cta::mediachanger::acs::daemon::msgTypeToString(msgType);
+      cta::exception::Exception ex;
+      ex.getMessage() << "Failed to dispatch message handler"
+        ": Unexpected request type: msgType=" << msgType << " msgTypeStr=" <<
+        msgTypeStr;
+      throw ex;
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleAcsMountTapeReadOnly
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsMessageHandler::
+  handleAcsMountTapeReadOnly(const cta::mediachanger::Frame &rqst) {
+  m_log(LOG_DEBUG, "Handling AcsMountTapeReadOnly message");
+
+  try {
+    cta::mediachanger::AcsMountTapeReadOnly rqstBody;
+    rqst.parseBodyIntoProtocolBuffer(rqstBody);
+    
+    const std::string vid = rqstBody.vid();
+    const uint32_t acs    = rqstBody.acs();
+    const uint32_t lsm    = rqstBody.lsm();
+    const uint32_t panel  = rqstBody.panel();
+    const uint32_t drive  = rqstBody.drive();
+    
+    std::list<log::Param> params = {log::Param("TPVID", vid),
+      log::Param("acs", acs),
+      log::Param("lsm", lsm),
+      log::Param("panel", panel),
+      log::Param("drive", drive)};
+    m_log(LOG_INFO, "Mount tape for read-only access", params);
+
+    cta::mediachanger::acs::AcsImpl acsWrapper;
+    cta::mediachanger::acs::daemon::AcsMountTapeReadOnly acsMountTapeReadOnly(vid, acs, lsm, 
+      panel, drive, acsWrapper, m_log, m_ctaConf);
+    try {
+      acsMountTapeReadOnly.execute();
+      m_log(LOG_INFO,"Tape successfully mounted for read-only access", params);
+    } catch (cta::exception::Exception &ne) {
+      m_log(LOG_ERR,"Tape mount for read-only access failed: "
+        + ne.getMessage().str(), params);  
+      throw;  
+    }     
+    const cta::mediachanger::Frame reply = createReturnValueFrame(0);
+    return reply;
+  } catch(cta::exception::Exception &ne) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to handle AcsMountTapeReadOnly message: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleAcsMountTapeReadWrite
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsMessageHandler::
+  handleAcsMountTapeReadWrite(const cta::mediachanger::Frame &rqst) {
+  m_log(LOG_DEBUG, "Handling AcsMountTapeReadWrite message");
+
+  try {
+    cta::mediachanger::AcsMountTapeReadWrite rqstBody;
+    rqst.parseBodyIntoProtocolBuffer(rqstBody);
+     
+    const std::string vid = rqstBody.vid();
+    const uint32_t acs    = rqstBody.acs();
+    const uint32_t lsm    = rqstBody.lsm();
+    const uint32_t panel  = rqstBody.panel();
+    const uint32_t drive  = rqstBody.drive();
+    
+    std::list<log::Param> params = {log::Param("TPVID", vid),
+      log::Param("acs", acs),
+      log::Param("lsm", lsm),
+      log::Param("panel", panel),
+      log::Param("drive", drive)};
+    m_log(LOG_INFO, "Mount tape for read/write access",params);
+
+    cta::mediachanger::acs::AcsImpl acsWrapper;
+    cta::mediachanger::acs::daemon::AcsMountTapeReadWrite acsMountTapeReadWrite(vid, acs,
+      lsm, panel, drive, acsWrapper, m_log, m_ctaConf);
+    try {
+      acsMountTapeReadWrite.execute();   
+      m_log(LOG_INFO,"Tape successfully mounted for read/write access", params);
+    } catch (cta::exception::Exception &ne) {
+      m_log(LOG_ERR,"Tape mount for read/write access failed: "
+        + ne.getMessage().str(), params);  
+      throw;  
+    }     
+    const cta::mediachanger::Frame reply = createReturnValueFrame(0);
+    return reply;
+  } catch(cta::exception::Exception &ne) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to handle AcsMountTapeReadWrite message: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleAcsDismountTape
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsMessageHandler::
+  handleAcsDismountTape(const cta::mediachanger::Frame& rqst) {
+  m_log(LOG_DEBUG, "Handling AcsDismountTape message");
+
+  try {
+    cta::mediachanger::AcsDismountTape rqstBody;
+    rqst.parseBodyIntoProtocolBuffer(rqstBody);
+
+    const std::string vid = rqstBody.vid();
+    const uint32_t acs    = rqstBody.acs();
+    const uint32_t lsm    = rqstBody.lsm();
+    const uint32_t panel  = rqstBody.panel();
+    const uint32_t drive  = rqstBody.drive();
+    
+    std::list<log::Param> params = {log::Param("TPVID", vid),
+      log::Param("acs", acs),
+      log::Param("lsm", lsm),
+      log::Param("panel", panel),
+      log::Param("drive", drive)};
+    m_log(LOG_INFO, "Dismount tape",params);
+
+    cta::mediachanger::acs::AcsImpl acsWrapper;
+    cta::mediachanger::acs::daemon::AcsDismountTape acsDismountTape(vid, acs, lsm, panel, drive,
+      acsWrapper, m_log, m_ctaConf);
+    try {
+      acsDismountTape.execute();
+      m_log(LOG_INFO,"Tape successfully dismounted", params);
+    } catch (cta::exception::Exception &ne) {
+      m_log(LOG_ERR,"Tape dismount failed: "+ne.getMessage().str(), params);  
+      throw;  
+    }    
+    const cta::mediachanger::Frame reply = createReturnValueFrame(0);
+    return reply;
+  } catch(cta::exception::Exception &ne) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to handle AcsDismountTape message: " <<
+      ne.getMessage().str();
+    throw ex;
+  } catch(...) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to handle AcsDismountTape message: " 
+                    << "Caught an unknown exception";
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleAcsForceDismountTape
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsMessageHandler::
+  handleAcsForceDismountTape(const cta::mediachanger::Frame& rqst) {
+  m_log(LOG_DEBUG, "Handling AcsDismountTape message");
+
+  try {
+    cta::mediachanger::AcsForceDismountTape rqstBody;
+    rqst.parseBodyIntoProtocolBuffer(rqstBody);
+
+    const std::string vid = rqstBody.vid();
+    const uint32_t acs    = rqstBody.acs();
+    const uint32_t lsm    = rqstBody.lsm();
+    const uint32_t panel  = rqstBody.panel();
+    const uint32_t drive  = rqstBody.drive();
+
+    std::list<log::Param> params = {log::Param("TPVID", vid),
+      log::Param("acs", acs),
+      log::Param("lsm", lsm),
+      log::Param("panel", panel),
+      log::Param("drive", drive)};
+    m_log(LOG_INFO, "Force dismount tape", params);
+
+    cta::mediachanger::acs::AcsImpl acsWrapper;
+    cta::mediachanger::acs::daemon::AcsForceDismountTape acsForceDismountTape(vid, acs, lsm,
+      panel, drive, acsWrapper, m_log, m_ctaConf);
+    try {
+      acsForceDismountTape.execute();
+      m_log(LOG_INFO,"Tape successfully force dismounted", params);
+    } catch (cta::exception::Exception &ne) {
+      m_log(LOG_ERR,"Tape force dismount failed: "+ne.getMessage().str(),
+        params);
+      throw;
+    }
+    const cta::mediachanger::Frame reply = createReturnValueFrame(0);
+    return reply;
+  } catch(cta::exception::Exception &ne) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to handle AcsForceDismountTape message: " <<
+      ne.getMessage().str();
+    throw ex;
+  } catch(...) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to handle AcsForceDismountTape message: "
+                    << "Caught an unknown exception";
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// createReturnValueFrame
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsMessageHandler::
+  createReturnValueFrame(const int value) {
+  cta::mediachanger::Frame frame;
+
+  frame.header = cta::mediachanger::protoTapePreFillHeader();
+  frame.header.set_msgtype(cta::mediachanger::MSG_TYPE_RETURNVALUE);
+  frame.header.set_bodyhashvalue(cta::mediachanger::computeSHA1Base64(frame.body));
+  frame.header.set_bodysignature("PIPO");
+
+  cta::mediachanger::ReturnValue body;
+  body.set_value(value);
+  frame.serializeProtocolBufferIntoBody(body);
+
+  return frame;
+}
+
+//------------------------------------------------------------------------------
+// createExceptionFrame
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsMessageHandler::
+  createExceptionFrame(const int code, const std::string& msg) {
+  cta::mediachanger::Frame frame;
+
+  frame.header = cta::mediachanger::protoTapePreFillHeader();
+  frame.header.set_msgtype(cta::mediachanger::MSG_TYPE_EXCEPTION);
+  frame.header.set_bodyhashvalue(cta::mediachanger::computeSHA1Base64(frame.body));
+  frame.header.set_bodysignature("PIPO");
+
+  cta::mediachanger::Exception body;
+  body.set_code(code);
+  body.set_message(msg);
+  frame.serializeProtocolBufferIntoBody(body);
+
+  return frame;
+}
diff --git a/mediachanger/acs/daemon/AcsMessageHandler.hpp b/mediachanger/acs/daemon/AcsMessageHandler.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..54990856429e8f94b3982c7cff961948a4185a3d
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsMessageHandler.hpp
@@ -0,0 +1,187 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/acs/Constants.hpp"
+#include "mediachanger/Frame.hpp"
+#include "mediachanger/ZmqSocket.hpp"
+#include "mediachanger/reactor/ZMQReactor.hpp"
+#include "AcsDaemon.hpp"
+#include "AcsdConfiguration.hpp"
+#include "AcsPendingRequests.hpp"
+#include "common/log/SyslogLogger.hpp"
+#include "mediachanger/reactor/ZMQPollEventHandler.hpp"
+
+namespace cta     {
+namespace mediachanger      {
+namespace acs     {
+namespace daemon     {
+
+/**
+ * Handles the events of the socket listening for connection from the tape 
+ * server daemon.
+ */
+class AcsMessageHandler: public cta::mediachanger::reactor::ZMQPollEventHandler {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param reactor The reactor to which new CTA ACS daemon connection 
+   * handlers are to be registered.
+   * @param hostName   The name of the host.
+   * @param zmqContext The ZMQ context.
+   * @param ctaConf The configuration for the CTA ACS daemon.
+   * @param acsPendingRequests The object to handle requests to the CTA ACS
+   * daemon.
+   */
+  AcsMessageHandler(
+    log::Logger &log,
+    cta::mediachanger::reactor::ZMQReactor &reactor,
+    void *const zmqContext,
+    const std::string &hostName,
+    const AcsdConfiguration &ctaConf,
+    AcsPendingRequests &acsPendingRequests);
+
+  /**
+   * Destructor.
+   */
+  ~AcsMessageHandler() throw();
+
+  /**
+   * Returns the human-readable name this event handler.
+   */
+  std::string getName() const throw();
+
+  /**
+   * Fills the specified poll file-descriptor ready to be used in a call to
+   * poll().
+   */
+  void fillPollFd(zmq_pollitem_t &fd) throw();
+
+  /**
+   * Handles the specified event.
+   *
+   * @param fd The poll file-descriptor describing the event.
+   * @return true if the event handler should be removed from and deleted by
+   * the reactor.
+   */
+  bool handleEvent(const zmq_pollitem_t &fd) throw();
+  
+private:
+
+  /**
+   * Creates a message frame containing a ReturnValue message.
+   *
+   * @param value The return value of the ReturnValue message.
+   * @return The message frame.
+   */
+  cta::mediachanger::Frame createReturnValueFrame(const int value);
+
+  /**
+   * Creates a message frame containing an Exception message.
+   *
+   * @param code The error code of the exception.
+   * @param msg The message string of the exception.
+   */
+  cta::mediachanger::Frame createExceptionFrame(const int code,
+    const std::string& msg);
+     
+  /**
+   * Make sure the  zmq_pollitem_t's socket is the same as m_socket
+   * Throw an exception if it is not the case
+   * @param fd the poll item 
+   */
+  void checkSocket(const zmq_pollitem_t &fd) const;
+  
+  /**
+   * Dispatches the appropriate handler method for the specified request
+   * message.
+   *
+   * @param  rqst The request.
+   * @return The reply.
+   */
+  cta::mediachanger::Frame dispatchMsgHandler(const cta::mediachanger::Frame &rqst) ;
+
+  /**
+   * Handles the mount tape for read-only.
+   *
+   * @param  rqst The request.
+   * @return The reply.
+   */
+  cta::mediachanger::Frame handleAcsMountTapeReadOnly(const cta::mediachanger::Frame &rqst);
+  
+  /**
+   * Handles the mount tape for read/write.
+   *
+   * @param  rqst The request.
+   * @return The reply.
+   */
+  cta::mediachanger::Frame handleAcsMountTapeReadWrite(const cta::mediachanger::Frame &rqst);
+
+  /**
+   * Handles the dismount tape request.
+   *
+   * @param rqst The request.
+   * @return The reply.
+   */
+  cta::mediachanger::Frame handleAcsDismountTape(const cta::mediachanger::Frame &rqst);
+
+  /**
+   * Handles the force dismount tape request.
+   *
+   * @param rqst The request.
+   * @return The reply.
+   */
+  cta::mediachanger::Frame handleAcsForceDismountTape(const cta::mediachanger::Frame &rqst);
+  
+  log::Logger &m_log;
+  /**
+   * The reactor to which new CTA ACS daemon connection handlers are to
+   * be registered.
+   */
+  cta::mediachanger::reactor::ZMQReactor &m_reactor;
+
+  /**
+   * The ZMQ socket listening for messages.
+   */
+  cta::mediachanger::ZmqSocketST m_socket;
+ 
+  /**
+   * The name of the host on which CTA ACS daemon is running.
+   */
+  const std::string m_hostName;
+  
+
+  /**
+   * The configuration parameters for the CTA ACS daemon.
+   */
+  const acs::daemon::AcsdConfiguration m_ctaConf;
+  
+  /**
+   * The object to handle requests to the CTA ACS daemon.
+   */
+  AcsPendingRequests &m_acsPendingRequests;
+  
+}; // class AcsMessageHandler
+
+} // namespace deamon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsMountTapeReadOnly.cpp b/mediachanger/acs/daemon/AcsMountTapeReadOnly.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b1b83efd0e578869751df799bb5ce4569295b54
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsMountTapeReadOnly.cpp
@@ -0,0 +1,213 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#include "AcsMountTapeReadOnly.hpp"
+#include "common/exception/MountFailed.hpp"
+#include "common/exception/QueryVolumeFailed.hpp"
+#include "common/log/log.hpp"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::AcsMountTapeReadOnly(
+  const std::string &vid,
+  const uint32_t acs,
+  const uint32_t lsm,
+  const uint32_t panel,
+  const uint32_t drive,
+  cta::mediachanger::acs::Acs &acsWrapper,
+  log::Logger& log,
+  const AcsdConfiguration &ctaConf):
+  AcsLibraryInteraction(acsWrapper, log),
+  m_volId(acsWrapper.str2Volid(vid)),
+  m_driveId(acsWrapper.alpd2DriveId(acs,lsm,panel,drive)),      
+  m_acsWrapper(acsWrapper),
+  m_log(log),
+  m_ctaConf(ctaConf) {    
+}
+
+//------------------------------------------------------------------------------
+// execute
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::execute() const {      
+  try {
+    syncMountTapeReadOnly();
+  } catch (cta::exception::MountFailed &mountFailed) {
+    try {
+      const std::string queryVolumeResponse = syncQueryVolume();      
+      mountFailed.getMessage() << " : The query volume response: " << 
+        queryVolumeResponse;
+    } catch (cta::exception::QueryVolumeFailed &queryFailed) {
+      mountFailed.getMessage() << " : " << queryFailed.getMessage().str();
+    } 
+    throw mountFailed;
+  }    
+}
+
+//------------------------------------------------------------------------------
+// syncMountTapeReadOnly
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::syncMountTapeReadOnly() const
+  {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendMountTapeReadOnlyRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, 
+      m_ctaConf.QueryInterval.value(), m_ctaConf.CmdTimeout.value());
+    processMountTapeReadOnlyResponse(buf);
+  }  catch(cta::exception::Exception &ex) {
+    cta::exception::MountFailed mf;
+    mf.getMessage() << "Failed to mount for read-only access volume " <<
+      m_volId.external_label << ": " << ex.getMessage().str();
+    throw mf;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendMountTapeReadOnlyRequest
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::sendMountTapeReadOnlyRequest(
+  const SEQ_NO seqNumber) const {
+  const LOCKID lockId    = 0; // No lock
+  const BOOLEAN bypass   = FALSE;
+  const BOOLEAN readOnly = TRUE;
+
+  m_log(LOG_DEBUG,"Calling Acs::mount()");
+  const STATUS s = m_acsWrapper.mount(seqNumber, lockId, m_volId,
+    m_driveId, readOnly, bypass);
+  std::stringstream dbgMsg;
+  dbgMsg << "Acs::mount() returned " << acs_status(s);            
+  m_log(LOG_DEBUG,dbgMsg.str());
+
+  if(STATUS_SUCCESS != s) {
+    cta::exception::MountFailed ex;
+    ex.getMessage() << "Failed to send request to mount for read-only access"
+      " volume " << m_volId.external_label << " into drive " <<
+      m_acsWrapper.driveId2Str(m_driveId) << ": readOnly=" <<
+      (readOnly ? "TRUE" : "FALSE") << ": " << acs_status(s);
+    throw ex;
+  } 
+}
+
+//------------------------------------------------------------------------------
+// processMountTapeReadOnlyResponse
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::processMountTapeReadOnlyResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const
+  {
+  const ACS_MOUNT_RESPONSE *const msg = (ACS_MOUNT_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->mount_status) {
+    cta::exception::MountFailed ex;
+    ex.getMessage() << "Status of mount response is not success: " << 
+      acs_status(msg->mount_status);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// syncQueryVolume
+//------------------------------------------------------------------------------
+std::string cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::syncQueryVolume() const {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+  try {
+    sendQueryVolumeRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, 
+      m_ctaConf.QueryInterval.value(), m_ctaConf.CmdTimeout.value());
+    return processQueryResponse(buf);
+  } catch(cta::exception::Exception &ex) {
+    cta::exception::QueryVolumeFailed qf;
+    qf.getMessage() << "Failed to query volume " <<
+      m_volId.external_label << ": " << ex.getMessage().str();
+    throw qf;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendQueryVolumeRequest
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::sendQueryVolumeRequest (
+  const SEQ_NO seqNumber) const {
+  VOLID volIds[MAX_ID];
+
+  memset(volIds, '\0', sizeof(volIds));
+  strncpy(volIds[0].external_label, m_volId.external_label,
+    sizeof(volIds[0].external_label));
+  volIds[0].external_label[sizeof(volIds[0].external_label) - 1]  = '\0';
+           
+  m_log(LOG_DEBUG,"Calling Acs::queryVolume()");
+    
+  const STATUS s = m_acs.queryVolume(seqNumber, volIds, 1);
+
+  std::stringstream dbgMsg;
+  dbgMsg << "Acs::queryVolume() returned " << acs_status(s);            
+  m_log(LOG_DEBUG,"Calling Acs::queryVolume()");
+
+  if(STATUS_SUCCESS != s) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Failed to send query request for volume " <<
+      m_volId.external_label << ": " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// processQueryResponse
+//------------------------------------------------------------------------------
+std::string cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::processQueryResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const {
+
+  const ACS_QUERY_VOL_RESPONSE *const msg = (ACS_QUERY_VOL_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->query_vol_status) {
+    cta::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) {
+    cta::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_volId.external_label, volStatus.vol_id.external_label)) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() <<
+      "Volume identifier of query response does not match that of request"
+      ": requestVID=" << m_volId.external_label <<
+      " responseVID=" << volStatus.vol_id.external_label;
+    throw ex;
+  }
+
+  return volumeStatusAsString(volStatus);
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsMountTapeReadOnly::~AcsMountTapeReadOnly() throw() {  
+}
diff --git a/mediachanger/acs/daemon/AcsMountTapeReadOnly.hpp b/mediachanger/acs/daemon/AcsMountTapeReadOnly.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..51019d304115f6a73c2d2afe5d99802e57d32bdd
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsMountTapeReadOnly.hpp
@@ -0,0 +1,142 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/acs/Acs.hpp"
+#include "AcsdConfiguration.hpp"
+#include "mediachanger/acs/AcsLibraryInteraction.hpp"
+
+namespace cta        {
+namespace mediachanger     {
+namespace acs        {
+namespace daemon	{
+
+/**
+ * Class responsible for mounting tapes for read-only access through ACS API.
+ */
+class AcsMountTapeReadOnly: public cta::mediachanger::acs::AcsLibraryInteraction {
+
+public:
+
+  /**
+   * Constructor.
+   */
+  AcsMountTapeReadOnly(
+    const std::string &vid,
+    const uint32_t acs,
+    const uint32_t lsm,
+    const uint32_t panel,
+    const uint32_t drive,
+    cta::mediachanger::acs::Acs &acsWrapper,
+    cta::log::Logger& log,
+    const AcsdConfiguration &ctaConf);
+
+  /**
+   * Destructor.
+   */
+  ~AcsMountTapeReadOnly() throw();
+
+  /**
+   * Execute mount request through ACS API.
+   * Throws cta::exception::Exception if the mount is not successful. Adds to
+   * the exception the result of the query volume request for the given volume.
+   */
+  void execute() const ;
+  
+protected:
+
+  /**
+   * 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 succeeded, failed or
+   * the specified timeout has been reached.
+   */
+  void syncMountTapeReadOnly() const;
+  
+  /**
+   * Sends the mount request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendMountTapeReadOnlyRequest(const SEQ_NO seqNumber) const;
+ 
+  /**
+   * Throws cta::exception::MountFailed if the mount was not
+   * successful.
+   *
+   * @param buf The mount-response message.
+   */
+  void processMountTapeReadOnlyResponse(
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const;
+  
+  /**
+   * Queries ACS for information about the volume identifier.
+   *
+   * 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 string presentation of the query volume response.
+   */
+  std::string syncQueryVolume() const;
+  
+  /**
+   * Sends the query volume  request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendQueryVolumeRequest(const SEQ_NO seqNumber) const;
+
+  /**
+   * Extracts the volume status from the specified query-response message and
+   * returns it in human-readable form.
+   *
+   * @param buf The query-response message.
+   * @return    The string presentation of the query volume response.
+   */
+  std::string processQueryResponse(
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const;
+  
+  /**
+   * ACS VOLID
+   */  
+  VOLID m_volId;
+  
+  /**
+   * ACS DRIVEID
+   */  
+  DRIVEID m_driveId;  
+  
+  /**
+   * Object providing c wrapper for ACS commands.
+   */
+  Acs &m_acsWrapper;
+  log::Logger& m_log;
+  /**
+   * The configuration parameters for the CTA ACS daemon.
+   */
+  const AcsdConfiguration m_ctaConf;
+
+}; // class AcsMountTapeReadOnly
+
+} // namespace daemon
+} // namepsace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsMountTapeReadWrite.cpp b/mediachanger/acs/daemon/AcsMountTapeReadWrite.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d7dc1ff4dd0768be486fa59637d086d61ebf1035
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsMountTapeReadWrite.cpp
@@ -0,0 +1,216 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#include "AcsMountTapeReadWrite.hpp"
+#include "common/exception/MountFailed.hpp"
+#include "mediachanger/acs/AcsLibraryInteraction.hpp"
+#include "common/exception/QueryVolumeFailed.hpp"
+#include "common/log/log.hpp"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::AcsMountTapeReadWrite(
+  const std::string &vid,
+  const uint32_t acs,
+  const uint32_t lsm,
+  const uint32_t panel,
+  const uint32_t drive,
+  cta::mediachanger::acs::Acs &acsWrapper,
+  cta::log::Logger &log,
+  const AcsdConfiguration &ctaConf):
+  AcsLibraryInteraction(acsWrapper, log),
+  m_volId(acsWrapper.str2Volid(vid)),
+  m_driveId(acsWrapper.alpd2DriveId(acs,lsm,panel,drive)), 
+  m_acsWrapper(acsWrapper),
+  m_log(log),
+  m_ctaConf(ctaConf) {
+}
+
+//------------------------------------------------------------------------------
+// execute
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::execute() const {
+  try {
+    syncMountTapeReadWrite();
+  } catch (cta::exception::MountFailed &mountFailed) {
+    try {
+      const std::string queryVolumeResponse = syncQueryVolume();
+      mountFailed.getMessage() << " : The query volume response: " << 
+        queryVolumeResponse;
+    } catch (cta::exception::QueryVolumeFailed &queryFailed) {
+      mountFailed.getMessage() << " : " << queryFailed.getMessage().str();
+    }
+    throw mountFailed;
+  }  
+}
+
+//------------------------------------------------------------------------------
+// syncMountTapeReadWrite
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::syncMountTapeReadWrite() const
+  {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+  try {
+    sendMountTapeReadWriteRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, 
+      m_ctaConf.QueryInterval.value(), m_ctaConf.CmdTimeout.value());
+    processMountTapeReadWriteResponse(buf);
+  }  catch(cta::exception::Exception &ex) {
+    cta::exception::MountFailed mf;
+    mf.getMessage() << "Failed to mount for read/write access volume " <<
+      m_volId.external_label << ": " << ex.getMessage().str();
+    throw mf;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendMountTapeReadWriteRequest
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::sendMountTapeReadWriteRequest(
+  const SEQ_NO seqNumber) const {
+  const LOCKID lockId    = 0; // No lock
+  const BOOLEAN bypass   = FALSE;
+  const BOOLEAN readOnly = FALSE;
+
+  m_log(LOG_DEBUG,"Calling Acs::mount()");
+  const STATUS s = m_acsWrapper.mount(seqNumber, lockId, m_volId,
+    m_driveId, readOnly, bypass);
+  std::stringstream dbgMsg;
+  dbgMsg << "Acs::mount() returned " << acs_status(s);          
+  m_log(LOG_DEBUG,dbgMsg.str());
+
+  if(STATUS_SUCCESS != s) {
+    cta::exception::MountFailed ex;
+    ex.getMessage() << "Failed to send request to mount for read/write access"
+      " volume " << m_volId.external_label << " into drive " 
+      << m_acsWrapper.driveId2Str(m_driveId) 
+      << ": readOnly=" 
+      << (readOnly ? "TRUE" : "FALSE") << ": " << acs_status(s);
+    throw ex;
+  } 
+}
+
+//------------------------------------------------------------------------------
+// processMountTapeReadWriteResponse
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::processMountTapeReadWriteResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const
+  {
+  const ACS_MOUNT_RESPONSE *const msg = (ACS_MOUNT_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->mount_status) {
+    cta::exception::MountFailed ex;
+    ex.getMessage() << "Status of mount response is not success: " << 
+      acs_status(msg->mount_status);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// syncQueryVolume
+//------------------------------------------------------------------------------
+std::string cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::syncQueryVolume() const {
+  const SEQ_NO requestSeqNumber = 1;
+  ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+  try {
+    sendQueryVolumeRequest(requestSeqNumber);
+    requestResponsesUntilFinal(requestSeqNumber, buf, 
+      m_ctaConf.QueryInterval.value(), m_ctaConf.CmdTimeout.value());
+    return processQueryResponse(buf);
+  } catch(cta::exception::Exception &ex) {
+    cta::exception::QueryVolumeFailed qf;
+    qf.getMessage() << "Failed to query volume " <<
+      m_volId.external_label << ": " << ex.getMessage().str();
+    throw qf;
+  }
+}
+
+//------------------------------------------------------------------------------
+// sendQueryVolumeRequest
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::sendQueryVolumeRequest (
+  const SEQ_NO seqNumber) const {
+  VOLID volIds[MAX_ID];
+
+  memset(volIds, '\0', sizeof(volIds));
+  strncpy(volIds[0].external_label, m_volId.external_label,
+    sizeof(volIds[0].external_label));
+  volIds[0].external_label[sizeof(volIds[0].external_label) - 1]  = '\0';
+           
+  m_log(LOG_DEBUG,"Calling Acs::queryVolume()");
+  
+  
+  const STATUS s = m_acs.queryVolume(seqNumber, volIds, 1);
+
+  std::stringstream dbgMsg;
+  dbgMsg << "Acs::queryVolume() returned " << acs_status(s);            
+  m_log(LOG_DEBUG,"Calling Acs::queryVolume()");
+
+  if(STATUS_SUCCESS != s) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() << "Failed to send query request for volume " <<
+      m_volId.external_label << ": " << acs_status(s);
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// processQueryResponse
+//------------------------------------------------------------------------------
+std::string cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::processQueryResponse(
+  ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const {
+
+  const ACS_QUERY_VOL_RESPONSE *const msg = (ACS_QUERY_VOL_RESPONSE *)buf;
+
+  if(STATUS_SUCCESS != msg->query_vol_status) {
+    cta::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) {
+    cta::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_volId.external_label, volStatus.vol_id.external_label)) {
+    cta::exception::QueryVolumeFailed ex;
+    ex.getMessage() <<
+      "Volume identifier of query response does not match that of request"
+      ": requestVID=" << m_volId.external_label <<
+      " responseVID=" << volStatus.vol_id.external_label;
+    throw ex;
+  }
+
+  return volumeStatusAsString(volStatus);
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsMountTapeReadWrite::~AcsMountTapeReadWrite() throw() {  
+}
diff --git a/mediachanger/acs/daemon/AcsMountTapeReadWrite.hpp b/mediachanger/acs/daemon/AcsMountTapeReadWrite.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a2d42306443ed192b0af9786c1e90f2141562fa1
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsMountTapeReadWrite.hpp
@@ -0,0 +1,142 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/acs/Acs.hpp"
+#include "AcsdConfiguration.hpp"
+#include "mediachanger/acs/AcsLibraryInteraction.hpp"
+
+namespace cta        {
+namespace mediachanger        {
+namespace acs     {
+namespace daemon        {
+
+/**
+ * Class responsible for mounting tapes for read/write access through ACS API.
+ */
+class AcsMountTapeReadWrite: public cta::mediachanger::acs::AcsLibraryInteraction {
+
+public:
+
+  /**
+   * Constructor.
+   */
+  AcsMountTapeReadWrite(
+    const std::string &vid,
+    const uint32_t acs,
+    const uint32_t lsm,
+    const uint32_t panel,
+    const uint32_t drive,
+    cta::mediachanger::acs::Acs &acsWrapper,
+    log::Logger& log,
+    const AcsdConfiguration &ctaConf);
+
+  /**
+   * Destructor.
+   */
+  ~AcsMountTapeReadWrite() throw();
+
+  /**
+   * Execute mount request through ACS API.
+   */
+  void execute() const;
+  
+protected:
+
+  /**
+   * 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 succeeded, failed or
+   * the specified timeout has been reached.
+   */
+  void syncMountTapeReadWrite() const;
+  
+  /**
+   * Sends the mount request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendMountTapeReadWriteRequest(const SEQ_NO seqNumber) const;
+ 
+  /**
+   * Throws cta::exception::MountFailed if the mount was not
+   * successful.
+   *
+   * @param buf The mount-response message.
+   */
+  void processMountTapeReadWriteResponse(
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const;
+  
+  /**
+   * Queries ACS for information about the volume identifier.
+   *
+   * 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 string presentation of the query volume response.
+   */
+  std::string syncQueryVolume() const;
+  
+  /**
+   * Sends the query volume  request to ACSLS.
+   *
+   * @param seqNumber The sequence number to be used in the request.
+   */
+  void sendQueryVolumeRequest(const SEQ_NO seqNumber) const;
+
+  /**
+   * Extracts the volume status from the specified query-response message and
+   * returns it in human-readable form.
+   *
+   * @param buf The query-response message.
+   * @return    The string presentation of the query volume response.
+   */
+  std::string processQueryResponse(
+    ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const;
+  
+  /**
+   * ACS VOLID
+   */  
+  VOLID m_volId;
+  
+  /**
+   * ACS DRIVEID
+   */  
+  DRIVEID m_driveId;  
+  
+  /**
+   * Object providing C wrapper for ACS commands.
+   */
+  Acs &m_acsWrapper;
+  
+  log::Logger& m_log;
+
+  /**
+   * The configuration parameters for the CTA ACS daemon.
+   */
+  const AcsdConfiguration m_ctaConf;
+  
+}; // class AcsMountTapeReadWrite
+
+} // namespace daemon
+} // namepsace acs 
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsPendingRequests.cpp b/mediachanger/acs/daemon/AcsPendingRequests.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fedb8521552783d50ad8480315cd46855ec2f04c
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsPendingRequests.cpp
@@ -0,0 +1,331 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AcsPendingRequests.hpp"
+#include "mediachanger/acs/AcsImpl.hpp"
+#include "mediachanger/acs/daemon/AcsRequest.hpp"
+#include "mediachanger/acs/daemon/AcsDismountTape.hpp"
+#include "AcsRequestDismountTape.hpp"
+#include "common/Constants.hpp"
+#include "mediachanger/Constants.hpp"
+#include "mediachanger/acs/daemon/Constants.hpp"
+#include "common/log/log.hpp"
+#include "mediachanger/messages.hpp"
+#include "mediachanger/Frame.hpp"
+#include "mediachanger/AcsDismountTape.pb.h"
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsPendingRequests::AcsPendingRequests(
+  const AcsdConfiguration &ctaConf, cta::log::Logger &l):
+  m_ctaConf(ctaConf),
+  m_log(l),
+  m_lastTimeResponseUsed(0) {
+}
+
+//-----------------------------------------------------------------------------
+// destructor
+//-----------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsPendingRequests::~AcsPendingRequests() throw() {
+  for(RequestList::const_iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();  itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    delete acsRequest;
+  }
+}
+
+//-----------------------------------------------------------------------------
+// tick
+//-----------------------------------------------------------------------------
+
+  void cta::mediachanger::acs::daemon::AcsPendingRequests::tick() {
+  bool haveRunningRequests = false;
+  for(RequestList::const_iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    acsRequest->tick();
+    if(acsRequest->isRunning()) {
+      haveRunningRequests = true;
+    }
+  }
+  
+  if (haveRunningRequests) {
+    const time_t now = time(0);
+    
+    const time_t secsSinceLastResponse = now -  m_lastTimeResponseUsed;
+    const bool responseTimeExceeded = secsSinceLastResponse >
+      ACS_RESPONSE_TIMEOUT;
+    
+    if (responseTimeExceeded) {
+      const int responseTimeout = 0 ; // 0 - means pool for 
+                                      // the existence of a response.
+
+      SEQ_NO responseSeqNumber = 0;
+      REQ_ID reqId = (REQ_ID)0;
+      ACS_RESPONSE_TYPE responseType = RT_NONE;
+      ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+
+      m_log(LOG_DEBUG,
+        "AcsPendingRequests::tick() Calling Acs::response()");
+
+      const STATUS responseStatus = m_acs.response(responseTimeout,
+        responseSeqNumber, reqId, responseType, buf);
+
+      if (STATUS_SUCCESS == responseStatus) {
+        setRequestResponse(responseSeqNumber,responseType, buf);
+      }
+      m_lastTimeResponseUsed = time(0);
+    }
+  }    
+}
+
+//-----------------------------------------------------------------------------
+// setRequestResponse
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsPendingRequests::setRequestResponse(
+  const SEQ_NO responseSeqNumber, const ACS_RESPONSE_TYPE responseType,
+  const ALIGNED_BYTES *const responseMsg) {
+  for(RequestList::const_iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    if ( responseSeqNumber == acsRequest->getSeqNo()) {
+      std::stringstream dbgMsg;
+      dbgMsg << "AcsPendingRequests::setRequestResponse responseType=" <<
+        responseType << " " << acsRequest->str();
+      m_log(LOG_DEBUG, dbgMsg.str());      
+      acsRequest->setResponse(responseType, responseMsg);      
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+// handleCompletedRequests
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsPendingRequests::handleCompletedRequests() {
+  for(RequestList::const_iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    if (acsRequest->isCompleted()) {
+      std::list<log::Param> params = {log::Param("TPVID", acsRequest->getVid()),
+        log::Param("acs", acsRequest->getAcs()),
+        log::Param("lsm", acsRequest->getLsm()),
+        log::Param("panel", acsRequest->getPanel()),
+        log::Param("drive", acsRequest->getDrive()),
+        log::Param("sender identity", acsRequest->getIdentity())
+      };
+      m_log(LOG_INFO,"Tape successfully dismounted",params);
+      acsRequest->sendReplayToClientOnce();
+      acsRequest->setStateToDelete();
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+// handleFailedRequests
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsPendingRequests::handleFailedRequests() {
+  for(RequestList::const_iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    if (acsRequest->isFailed()) {
+      std::list<log::Param> params = {log::Param("TPVID", acsRequest->getVid()),
+        log::Param("acs", acsRequest->getAcs()),
+        log::Param("lsm", acsRequest->getLsm()),
+        log::Param("panel", acsRequest->getPanel()),
+        log::Param("drive", acsRequest->getDrive()),
+        log::Param("sender identity", acsRequest->getIdentity())
+      };    
+      m_log(LOG_INFO,"Dismount tape failed", params);
+      acsRequest->sendReplayToClientOnce();
+      acsRequest->setStateToDelete();
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+// handleToDeleteRequests
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsPendingRequests::handleToDeleteRequests() {
+  for(RequestList::iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    if (acsRequest->isToDelete()) {
+      m_log(LOG_DEBUG,"AcsPendingRequests::handleToDeleteRequests " +
+        acsRequest->str());     
+      delete acsRequest;
+      itor=m_acsRequestList.erase(itor);      
+    }    
+  }
+}
+
+//-----------------------------------------------------------------------------
+// checkAndAddRequest
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsPendingRequests::checkAndAddRequest(
+ mediachanger::ZmqMsg &address, mediachanger::ZmqMsg &empty,
+ const cta::mediachanger::Frame &rqst, cta::mediachanger::ZmqSocketST &socket) {
+  std::list<log::Param> params = {
+    log::Param("sender identity", 
+      utils::hexDump(address.getData(), address.size()))
+  };
+  m_log(LOG_DEBUG, "AcsPendingRequests::checkAndAddRequest", params);
+
+  const cta::mediachanger::acs::daemon::MsgType msgType = (cta::mediachanger::acs::daemon::MsgType)rqst.header.msgtype();
+  switch(msgType) {
+  case mediachanger::MSG_TYPE_ACSMOUNTTAPEREADONLY:
+  case mediachanger::MSG_TYPE_ACSMOUNTTAPEREADWRITE:
+    {  
+      cta::exception::Exception ex;
+      ex.getMessage() << "Failed to check request"
+        ": Handling of this message type is not implemented: msgtype=" <<
+        rqst.header.msgtype();
+      throw ex;      
+    }
+  case mediachanger::MSG_TYPE_ACSDISMOUNTTAPE:
+    checkAndAddRequestDismountTape(address, empty, rqst, socket);
+    break;
+  default:
+    {
+      const std::string msgTypeStr = cta::mediachanger::acs::daemon::msgTypeToString(msgType);
+      cta::exception::Exception ex;
+      ex.getMessage() << "Failed to check request"
+        ": Unexpected request type: msgType=" << msgType << " msgTypeStr=" <<
+        msgTypeStr;
+      throw ex;
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+// checkAndAddRequestDismountTape
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsPendingRequests::checkAndAddRequestDismountTape(
+  mediachanger::ZmqMsg &address,  mediachanger::ZmqMsg &empty,
+  const mediachanger::Frame &rqst, mediachanger::ZmqSocketST &socket) {
+  m_log(LOG_DEBUG, 
+    "AcsPendingRequests::checkAndAddRequestDismountTape");
+    
+  mediachanger::AcsDismountTape rqstBody;
+  rqst.parseBodyIntoProtocolBuffer(rqstBody);
+
+  const std::string vid = rqstBody.vid();
+  const uint32_t acs    = rqstBody.acs();
+  const uint32_t lsm    = rqstBody.lsm();
+  const uint32_t panel  = rqstBody.panel();
+  const uint32_t drive  = rqstBody.drive();
+
+  checkRequest(vid, acs, lsm, panel, drive);
+  
+  std::list<log::Param> params = {log::Param("TPVID", vid),
+    log::Param("acs", acs),
+    log::Param("lsm", lsm),
+    log::Param("panel", panel),
+    log::Param("drive", drive),
+    log::Param("sender identity", 
+      utils::hexDump(address.getData(), address.size()))
+  };
+  m_log(LOG_INFO, "Dismount tape", params);
+   
+  const SEQ_NO seqNo = getSequenceNumber();
+  std::list<log::Param> seqParam = {log::Param("seqNumber", seqNo)};
+  m_log(LOG_DEBUG, "ACS sequence number", seqParam);  
+
+  try {
+    cta::mediachanger::acs::daemon::AcsRequest * acsRequestDismountTape = 
+      new AcsRequestDismountTape(vid, acs, lsm, panel, drive, 
+      //new AcsRequestDismountTape AcsRequestDismountTape(vid, acs, lsm, panel, drive, 
+        m_ctaConf, socket, address, empty, m_log, seqNo);
+    
+    acsRequestDismountTape->setStateToExecute(); 
+    m_acsRequestList.push_back(acsRequestDismountTape); 
+  } catch (cta::exception::Exception &ne) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to add dismount request: "
+      << ne.getMessage().str();
+    m_log(LOG_ERR, ex.getMessage().str());  
+    throw ex;  
+  }
+}
+
+//-----------------------------------------------------------------------------
+// checkRequest
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsPendingRequests::checkRequest(const std::string &vid, 
+  const uint32_t acs, const uint32_t lsm, const uint32_t panel,
+  const uint32_t drive) const {
+  for(RequestList::const_iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    if (acs == acsRequest->getAcs() && lsm == acsRequest->getLsm() &&
+      panel == acsRequest->getPanel() && drive == acsRequest->getDrive()) {   
+      cta::exception::Exception ex;
+      ex.getMessage() << "Check request failed: "
+        "acs, lsm, panel, drive already are used by another request: "<<
+         acsRequest->str();
+      throw ex; 
+    }
+    if (std::string::npos !=  vid.find(acsRequest->getVid())) {
+      cta::exception::Exception ex;
+      ex.getMessage() << "Check request failed: "
+        "vid already is used by another request: "<<
+         acsRequest->str();
+      throw ex; 
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+// getSequenceNumber
+//-----------------------------------------------------------------------------
+SEQ_NO cta::mediachanger::acs::daemon::AcsPendingRequests::getSequenceNumber() const {
+  unsigned short maxSeqNo = 0;
+  unsigned short minSeqNo = ACS_MAX_SEQ;
+  
+  for(RequestList::const_iterator itor = m_acsRequestList.begin(); 
+    itor != m_acsRequestList.end();itor++) {
+    cta::mediachanger::acs::daemon::AcsRequest *const acsRequest = *itor;
+    if (maxSeqNo < acsRequest->getSeqNo()) {
+      maxSeqNo = acsRequest->getSeqNo();
+    }
+    if (minSeqNo > acsRequest->getSeqNo()) {
+      minSeqNo = acsRequest->getSeqNo();
+    }
+  }
+  
+  // first request
+  if(ACS_MAX_SEQ == minSeqNo && 0 == maxSeqNo) {
+    return 1;
+  }
+    
+  // try to get number from 1 to minSeqNo
+  if(1 != minSeqNo ) {
+      return minSeqNo-1;
+    }
+  
+  // try to get number from maxSeqNo to maximum allowed
+  if (ACS_MAX_SEQ != maxSeqNo) {
+    return maxSeqNo+1;
+  }
+  
+  cta::exception::Exception ex;
+  ex.getMessage() << "Failed to get sequence number for ACS"
+    ": allocated minimum seqNo=\""<<minSeqNo<<"\""<<
+    " allocated maximum seqNo=\""<<maxSeqNo<<"\"";
+  m_log(LOG_ERR, ex.getMessage().str());  
+  throw ex;   
+}
diff --git a/mediachanger/acs/daemon/AcsPendingRequests.hpp b/mediachanger/acs/daemon/AcsPendingRequests.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8031001f65256b60c5e4070c46f127774ea72ad5
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsPendingRequests.hpp
@@ -0,0 +1,196 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/acs/daemon/AcsdConfiguration.hpp"
+#include "common/log/log.hpp"
+#include "common/log/SyslogLogger.hpp"
+#include "mediachanger/acs/daemon/AcsRequest.hpp"
+#include "mediachanger/acs/AcsImpl.hpp"
+#include "mediachanger/messages.hpp"
+#include "mediachanger/Frame.hpp"
+#include "mediachanger/ZmqMsg.hpp"
+#include "mediachanger/ZmqSocket.hpp"
+#include "mediachanger/ZmqSocketST.hpp"
+#include <list>
+#include <time.h>
+
+namespace cta     {
+namespace mediachanger        {
+namespace acs        {
+namespace daemon        {
+   
+/**
+ * Class responsible for keeping track of the Acs requests  controlled by
+ * the CTA ACS daemon.
+ */
+class AcsPendingRequests {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param ctaConf The configuration for the CTA ACS daemon.
+   */
+  AcsPendingRequests(const AcsdConfiguration &ctaConf, cta::log::Logger &);
+  
+  /**
+   * Destructor.
+   */
+  ~AcsPendingRequests() throw();
+
+  /**
+   * Notifies the AcsPendingRequests that it should perform any time related 
+   * actions.
+   *
+   * This method does not have to be called at any time precise interval.
+   */
+  void tick();
+
+  /**
+   * Requests to the CTA ACS daemon might have several states.
+   * 
+   *  ACS_REQUEST_TO_EXECUTE - is initial state. When request arrives from a 
+   *    client it is set to be asynchronous executed to ACS Library.
+   *  ACS_REQUEST_IS_RUNNING - the state in which we periodically query ACS 
+   *    Library to check the status of the ongoing request.
+   *  ACS_REQUEST_COMPLETED  - indicates that the request is completed 
+   *    successfully.
+   *  ACS_REQUEST_FAILED     - indicates that the request is completed 
+   *    unsuccessfully.
+   *  ACS_REQUEST_TO_DELETE  - indicates that the request is handled and might 
+   *    be deleted.
+   * 
+   *                             /- COMPLETED -\ 
+   * TO_EXECUTE -> IS_RUNNING ->|               |-> TO_DELETE
+   *                             \- FAILED    -/
+   */
+  
+  /**
+   * Handles successfully completed requests.
+   */
+  void handleCompletedRequests();
+  
+  /**
+   * Handles failed requests.
+   */
+  void handleFailedRequests();
+  
+  /**
+   * Performs cleanup for deleted requests.
+   */
+  void handleToDeleteRequests();
+  
+  /**
+   * Performs general checks for the incoming requests and calls next checker 
+   * for the message. Throws exceptions if checks are not passed.
+   * 
+   * @param address ZMQ message with client address.
+   * @param empty   ZMQ empty message.
+   * @param rqst    ZMQ message with CTA frame.
+   * @param socket  ZMQ socket to use.
+   */
+
+   void checkAndAddRequest(mediachanger::ZmqMsg &address,
+    mediachanger::ZmqMsg &empty,
+    const mediachanger:: Frame &rqst,
+    mediachanger::ZmqSocketST &socket);
+  
+/**
+   * Performs dismount specific checks for the incoming request and add it to 
+   * the list of the request to be handled.
+   * 
+   * @param address ZMQ message with client address.
+   * @param empty   ZMQ empty message.
+   * @param rqst    ZMQ message with CTA frame.
+   * @param socket  ZMQ socket to use.
+   */
+  void checkAndAddRequestDismountTape(mediachanger::ZmqMsg &address,
+    mediachanger::ZmqMsg &empty,
+    const mediachanger::Frame &rqst,
+    mediachanger::ZmqSocketST &socket);
+  
+  /**
+   * Find and return free sequence number for the ACS request.
+   * 
+   * @return The value of free sequence number for the ACS request. Throws
+   * exception if the is no free sequence number.
+   */
+  SEQ_NO getSequenceNumber() const;
+
+  /**
+   * Sets the type of the response and the response message in the ACS request
+   * with the sequence number equal response sequence number.
+   * 
+   * @param responseSeqNumber The sequence number to find ongoing ACS request.
+   * @param responseType The type of the response message.
+   * @param responseMsg  The response message
+   */ 
+   void setRequestResponse(const SEQ_NO responseSeqNumber,
+   const ACS_RESPONSE_TYPE responseType, 
+   const  ALIGNED_BYTES *const responseMsg);
+    
+  /**
+   * Performs checks for the request before adding it to the ACS requests list.
+   * Throws exceptions if there are any problems.
+   * 
+   * @param vid     The vid of the ACS request.
+   * @param acs     The acs value of the ACS request.
+   * @param lsm     The lsm value of the ACS request.
+   * @param panel   The panel value of the ACS request.
+   * @param drive   The drive value of the ACS request.
+   */ 
+  void checkRequest(const std::string &vid, const uint32_t acs, 
+    const uint32_t lsm, const uint32_t panel, const uint32_t drive) const ;
+  
+private:
+
+  /**
+   * The object representing cta configuration parameters for 
+   * the CTA ACS daemon.
+   */
+  const AcsdConfiguration m_ctaConf;
+  
+  log::Logger &m_log;
+  /**
+   * Type for the list of the ACS requests.
+   */
+  typedef std::list<AcsRequest *> RequestList;
+  
+  /**
+   * The list for the ACS requests.
+   */
+  RequestList m_acsRequestList;
+  
+  /**
+   * The ACLS C-API wrapper.
+   */  
+  AcsImpl m_acs;
+  
+  /**
+   * The time when the last ACS response command was used.
+   */ 
+  time_t m_lastTimeResponseUsed;
+  
+}; // class AcsPendingRequests
+
+} // namespace deamon
+} // namepsace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsRequest.cpp b/mediachanger/acs/daemon/AcsRequest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..572b2812d59eff684395753ce1d4a4364fcb3362
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsRequest.cpp
@@ -0,0 +1,283 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AcsRequest.hpp"
+#include "mediachanger/messages.hpp"
+#include "mediachanger/ReturnValue.pb.h"
+#include "mediachanger/Exception.pb.h"
+#include "common/utils/utils.hpp"
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsRequest::AcsRequest(cta::mediachanger::ZmqSocketST &socket,
+  cta::mediachanger::ZmqMsg &address,  cta::mediachanger::ZmqMsg &empty,
+  const SEQ_NO seqNo, const std::string vid, const uint32_t acs,
+  const uint32_t lsm, const uint32_t panel, const uint32_t drive):
+  m_seqNo(seqNo), 
+  m_vid(vid), 
+  m_acs(acs), 
+  m_lsm(lsm),
+  m_panel(panel),
+  m_drive(drive), 
+  m_socket(socket),
+  m_identity(utils::hexDump(address.getData(),address.size())),
+  m_isReplaySent(false) {
+    zmq_msg_init_size (&m_addressMsg, address.size());
+    memcpy (zmq_msg_data (&m_addressMsg), (const void*)&address.getZmqMsg(), 
+      address.size());
+
+    zmq_msg_init_size (&m_emptyMsg, empty.size());
+    memcpy (zmq_msg_data (&m_emptyMsg), (const void*)&empty.getZmqMsg(),
+      empty.size());
+}
+
+//-----------------------------------------------------------------------------
+// destructor
+//-----------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsRequest::~AcsRequest() {
+    zmq_msg_close(&m_addressMsg);
+    zmq_msg_close(&m_emptyMsg);  
+}
+
+//-----------------------------------------------------------------------------
+// isToExecute
+//-----------------------------------------------------------------------------
+bool cta::mediachanger::acs::daemon::AcsRequest::isToExecute() const throw () {
+  if (ACS_REQUEST_TO_EXECUTE == m_state) {
+    return true;
+  } else {
+    return false;
+  };
+}
+
+//-----------------------------------------------------------------------------
+// isRunning
+//-----------------------------------------------------------------------------
+bool cta::mediachanger::acs::daemon::AcsRequest::isRunning() const  throw () {
+  if (ACS_REQUEST_IS_RUNNING == m_state) {
+    return true;
+  } else {
+    return false;
+  };
+}
+
+//-----------------------------------------------------------------------------
+// isCompleted
+//-----------------------------------------------------------------------------
+bool cta::mediachanger::acs::daemon::AcsRequest::isCompleted() const throw () {
+  if (ACS_REQUEST_COMPLETED == m_state) {
+    return true;
+  } else {
+    return false;
+  };
+}
+
+//-----------------------------------------------------------------------------
+// isFailed
+//-----------------------------------------------------------------------------
+bool cta::mediachanger::acs::daemon::AcsRequest::isFailed() const  throw () {
+  if (ACS_REQUEST_FAILED == m_state) {
+    return true;
+  } else {
+    return false;
+  };
+}
+
+//-----------------------------------------------------------------------------
+// isToDelete
+//-----------------------------------------------------------------------------
+bool cta::mediachanger::acs::daemon::AcsRequest::isToDelete() const  throw () {
+  if (ACS_REQUEST_TO_DELETE == m_state) {
+    return true;
+  } else {
+    return false;
+  };
+}
+
+//------------------------------------------------------------------------------
+// createReturnValueFrame
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsRequest::createReturnValueFrame(
+  const int value) {
+  cta::mediachanger::Frame frame;
+
+  frame.header = cta::mediachanger::protoTapePreFillHeader();
+  frame.header.set_msgtype(cta::mediachanger::MSG_TYPE_RETURNVALUE);
+  frame.header.set_bodyhashvalue(
+    cta::mediachanger::computeSHA1Base64(frame.body));
+  frame.header.set_bodysignature("PIPO");
+
+  cta::mediachanger::ReturnValue body;
+  body.set_value(value);
+  frame.serializeProtocolBufferIntoBody(body);
+
+  return frame;
+}
+
+//------------------------------------------------------------------------------
+// createExceptionFrame
+//------------------------------------------------------------------------------
+cta::mediachanger::Frame cta::mediachanger::acs::daemon::AcsRequest::
+  createExceptionFrame(const int code, const std::string& msg) {
+  cta::mediachanger::Frame frame;
+
+  frame.header = cta::mediachanger::protoTapePreFillHeader();
+  frame.header.set_msgtype(mediachanger::MSG_TYPE_EXCEPTION);
+  frame.header.set_bodyhashvalue(mediachanger::computeSHA1Base64(frame.body));
+  frame.header.set_bodysignature("PIPO");
+
+  cta::mediachanger::Exception body;
+  body.set_code(code);
+  body.set_message(msg);
+  frame.serializeProtocolBufferIntoBody(body);
+
+  return frame;
+}
+
+
+//-----------------------------------------------------------------------------
+// sendReplayToClientOnce
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequest::sendReplayToClientOnce() {
+  if(m_isReplaySent) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to send second replay to the client";
+    throw ex;
+  }
+  m_socket.send(&m_addressMsg,ZMQ_SNDMORE);
+  m_socket.send(&m_emptyMsg,ZMQ_SNDMORE);
+
+  //messages::sendFrame(m_socket, m_reply);
+  sendFrame(m_socket, m_reply);
+}
+
+//-----------------------------------------------------------------------------
+// getIdentity
+//-----------------------------------------------------------------------------
+std::string cta::mediachanger::acs::daemon::AcsRequest::getIdentity() const throw() {
+  return m_identity;
+}
+
+//-----------------------------------------------------------------------------
+// str
+//-----------------------------------------------------------------------------
+std::string cta::mediachanger::acs::daemon::AcsRequest::str() const {
+  std::ostringstream oss;
+  oss << "vid=" << m_vid << " acs=" << m_acs << " lsm=" << m_lsm << " panel=" <<
+    m_panel << " drive=" << m_drive << " identity=" << getIdentity();
+  return oss.str();
+}
+
+//------------------------------------------------------------------------------
+// getVid
+//------------------------------------------------------------------------------
+const std::string &cta::mediachanger::acs::daemon::AcsRequest::getVid() const throw () {
+  return m_vid;
+}
+
+//------------------------------------------------------------------------------
+// getAcs
+//------------------------------------------------------------------------------
+uint32_t cta::mediachanger::acs::daemon::AcsRequest::getAcs() const throw () {
+  return m_acs;
+}
+
+//------------------------------------------------------------------------------
+// getLsm
+//------------------------------------------------------------------------------
+uint32_t cta::mediachanger::acs::daemon::AcsRequest::getLsm() const throw () {
+  return m_lsm;
+}
+
+//------------------------------------------------------------------------------
+// getPanel
+//------------------------------------------------------------------------------
+uint32_t cta::mediachanger::acs::daemon::AcsRequest::getPanel() const throw () {
+  return m_panel;
+}
+
+//------------------------------------------------------------------------------
+// getDrive
+//------------------------------------------------------------------------------
+uint32_t cta::mediachanger::acs::daemon::AcsRequest::getDrive() const throw () {
+  return m_drive;
+}
+
+
+//------------------------------------------------------------------------------
+// getSeqNo
+//------------------------------------------------------------------------------
+SEQ_NO cta::mediachanger::acs::daemon::AcsRequest::getSeqNo() const throw () {
+  return m_seqNo; 
+}
+
+//------------------------------------------------------------------------------
+// setResponse
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequest::setResponse(
+  const ACS_RESPONSE_TYPE responseType, 
+  const ALIGNED_BYTES *const responseMsg) throw () {
+  m_responseType = responseType;
+  memcpy(m_responseMsg,responseMsg,MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES));
+}
+
+//------------------------------------------------------------------------------
+// setStateFailed
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequest::setStateFailed(const int code,
+  const std::string& msg) {
+  m_reply = createExceptionFrame(code, msg);
+  m_state = ACS_REQUEST_FAILED;
+}
+
+//------------------------------------------------------------------------------
+// setStateCompleted
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequest::setStateCompleted() {
+  m_reply = createReturnValueFrame(0);
+  m_state = ACS_REQUEST_COMPLETED;       
+}
+
+//------------------------------------------------------------------------------
+// setStateIsRunning
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequest::setStateIsRunning() {
+  if (isToExecute()) {
+    m_state = ACS_REQUEST_IS_RUNNING;
+  } else {
+    cta::exception::Exception ex;
+    ex.getMessage() << "Failed to set request state to running from state="<<
+      m_state;
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// setStateToExecute
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequest::setStateToExecute() throw() {
+  m_state = ACS_REQUEST_TO_EXECUTE;       
+}
+
+//------------------------------------------------------------------------------
+// setStateToDelete
+//------------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequest::setStateToDelete() throw() {
+  m_state = ACS_REQUEST_TO_DELETE;       
+}
diff --git a/mediachanger/acs/daemon/AcsRequest.hpp b/mediachanger/acs/daemon/AcsRequest.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..52b22b94990f10b4af45619be8bbda92c1dcda6e
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsRequest.hpp
@@ -0,0 +1,317 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/Constants.hpp"
+#include "mediachanger/acs/Constants.hpp"
+#include "mediachanger/messages.hpp"
+#include "mediachanger/ZmqSocket.hpp"
+#include "mediachanger/ZmqSocketST.hpp"
+#include "mediachanger/acs/AcsImpl.hpp"
+#include "common/exception/Exception.hpp"
+#include <time.h>
+
+namespace cta     {
+namespace mediachanger        {
+namespace acs     {
+namespace daemon	{
+  
+  
+/**
+ * Abstract class defining the ACS request presentation to be used in the list
+ * of pending ACS requests.
+ */
+class AcsRequest {
+public:
+  
+  /**
+   * Constructor.
+   * 
+   * @param socket  ZMQ socket to use.
+   * @param address ZMQ message with client address.
+   * @param empty   ZMQ empty message.
+   * @param seqNo   Sequence number for the ACS request.
+   * @param vid     The vid of the ACS request.
+   * @param acs     The acs value of the ACS request.
+   * @param lsm     The lsm value of the ACS request.
+   * @param panel   The panel value of the ACS request.
+   * @param drive   The drive value of the ACS request.
+   */
+  AcsRequest(ZmqSocketST &socket, ZmqMsg &address,
+    ZmqMsg &empty, const SEQ_NO seqNo,
+    const std::string vid, const uint32_t acs,
+    const uint32_t lsm, const uint32_t panel, const uint32_t drive);
+  
+  /**
+   * Destructor.
+   */
+  virtual ~AcsRequest() = 0;
+
+  /**
+   * Perform any time related actions with the request to CTA ACS daemon.
+   *
+   * This method does not have to be called at any time precise interval.
+   */
+  virtual void tick() = 0;
+  
+  /**
+   * Checks if the ACS request is in to be executed state.
+   * 
+   * @return true if the ACS request in state to Execute.
+   */  
+  bool isToExecute() const throw();  
+  
+  /**
+   * Sets state of the ACS request to be executed.
+   */
+  void setStateToExecute() throw();
+  
+  /**
+   * Checks if the ACS request is in running state.
+   * 
+   * @return true if the ACS request is in running state.
+   */  
+  bool isRunning() const throw();
+  
+  /**
+   * Sets state of the ACS request to running.
+   */
+  void setStateIsRunning();
+  
+  /**
+   * Checks if the ACS request is in completed state.
+   * 
+   * @return true if the ACS request is in completed state.
+   */  
+  bool isCompleted() const throw();
+  
+  /**
+   * Sets state of the ACS request to completed and fills ZMQ replay frame with 
+   * good status 0.
+   */
+  void setStateCompleted();
+  
+  /**
+   * Checks if the ACS request is in failed state.
+   * 
+   * @return true if the ACS request is in failed state.
+   */  
+  bool isFailed() const throw();
+  
+  /**
+   * Sets state of the ACS request to failed and fills ZMQ replay frame with
+   * exception data.
+   */
+  void setStateFailed(const int code,const std::string& msg);
+  
+  /**
+   * Checks if the ACS request is in to delete state.
+   * 
+   * @return true if the ACS request is in to delete state.
+   */  
+  bool isToDelete() const throw();
+  
+  /**
+   * Sets state of the ACS request to be deleted.
+   */
+  void setStateToDelete() throw();
+
+  /**
+   * Gets the vid component of the ACS request.
+   *
+   * @return the vid component of the ACS request.
+   */
+  const std::string &getVid() const throw ();
+  
+  /**
+   * Gets the acs component of the ACS request.
+   *
+   * @return the acs component of the ACS request.
+   */
+  uint32_t getAcs() const throw ();
+
+  /**
+   * Gets the lsm component of the ACS request.
+   *
+   * @return the lsm component of the ACS request.
+   */
+  uint32_t getLsm() const throw ();
+
+  /**
+   * Gets the panel component of the ACS request.
+   *
+   * @return the panel component of the ACS request.
+   */
+  uint32_t getPanel() const throw ();
+
+  /**
+   * Gets the drive component of the ACS request.
+   *
+   * @return the drive component of the ACS request.
+   */
+  uint32_t getDrive() const throw ();
+  
+  /**
+   * Gets the SeqNumber component of the ACS request.
+   *
+   * @return the SeqNo component of the ACS request.
+   */
+  SEQ_NO getSeqNo() const throw ();
+  
+  /**
+   * Sets the fields of the response message of the ACS response request.
+   * 
+   * @param responseType The type of the response message.
+   * @param responseMsg  The response message.
+   */
+  void setResponse(const ACS_RESPONSE_TYPE responseType,
+    const ALIGNED_BYTES *const responseMsg) throw ();
+  
+  /**
+   * Send a replay to the client who issued the ACS request.
+   */
+  void sendReplayToClientOnce();
+  
+  /**
+   * Returns string presentation for the connection identity with the client.
+   * 
+   * @return The connection Identity.
+   */
+  std::string getIdentity() const throw();
+  
+  /**
+   * Returns a string representing ACS request.
+   * 
+   * @return The value of string presentation for the request 
+   */
+  std::string str() const;
+  
+  /**
+   * Abstract method to be implemented in concrete implementation. Checks 
+   * the status of the response from the response message buffer and the type of
+   * the response. Throws exception if the type of the response is RT_FINAL but
+   * the status is not success.
+   * 
+   * @return true if the type of response RT_FINAL and the response status 
+   *         is STATUS_SUCCESS.
+   */
+  virtual bool isResponseFinalAndSuccess() const = 0; 
+   
+private:
+
+  /**
+   * Creates a message frame containing a ReturnValue message.
+   *
+   * @param value The return value of the ReturnValue message.
+   * @return The message frame.
+   */
+  //cta::messages::Frame createReturnValueFrame(const int value);
+  Frame createReturnValueFrame(const int value);
+  
+  /**
+   * Creates a message frame containing an Exception message.
+   *
+   * @param code The error code of the exception.
+   * @param msg The message string of the exception.
+   */
+  Frame createExceptionFrame(const int code, 
+    const std::string& msg);
+  
+  /**
+   * Request sequence number of the ACS request.
+   */
+  const SEQ_NO m_seqNo;
+  
+  /**
+   * The vid component of the ACS request.
+   */
+  const std::string m_vid;
+
+  /**
+   * The acs component of the ACS request.
+   */
+  const uint32_t m_acs;
+  
+  /**
+   * The lsm component of the ACS request.
+   */
+  const uint32_t m_lsm;
+  
+  /**
+   * The panel component of the ACS request.
+   */
+  const uint32_t m_panel;
+  
+  /**
+   * The drive component of the ACS request.
+   */
+  const uint32_t m_drive;
+  
+  /**
+   * Internal state of the ACS request.
+   */  
+  RequestState m_state ; 
+  
+  /**
+   * Replay ZMQ frame for the client.
+   */
+  Frame m_reply;
+    
+  /**
+   * The ZMQ socket listening for messages.
+   */
+  ZmqSocketST &m_socket;
+  
+  /**
+   * Client identity for logging.
+   */  
+  const std::string m_identity;
+  
+  /**
+   * ZMQ address message for the client.
+   */
+  zmq_msg_t m_addressMsg;
+  
+  /**
+   * ZMQ empty message for the client.
+   */
+  zmq_msg_t m_emptyMsg;
+  
+  /**
+   * Store is the replay to the client has been sent or not.
+   */
+  bool m_isReplaySent;
+  
+protected:  
+  /**
+   * The type of the response message associated with the ACS request.
+   */
+  ACS_RESPONSE_TYPE m_responseType;
+  
+  /**
+   * The response message associated with the ACS request.
+   */
+  ALIGNED_BYTES m_responseMsg[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
+  
+}; // class AcsRequest
+
+} // namepsace daemon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsRequestDismountTape.cpp b/mediachanger/acs/daemon/AcsRequestDismountTape.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..74ed0fa0a952acb49bc121fc81033c3d43c69576
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsRequestDismountTape.cpp
@@ -0,0 +1,112 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AcsRequestDismountTape.hpp"
+#include "common/exception/DismountFailed.hpp"
+#include "common/log/log.hpp"
+
+//-----------------------------------------------------------------------------
+// constructor 
+//-----------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsRequestDismountTape::AcsRequestDismountTape(
+  const std::string &vid, const uint32_t acs,
+  const uint32_t lsm, const uint32_t panel, const uint32_t drive, 
+  const AcsdConfiguration &ctaConf,
+  mediachanger::ZmqSocketST &socket,
+  mediachanger::ZmqMsg &address,
+  mediachanger::ZmqMsg &empty,
+  log::Logger &log,
+  const SEQ_NO seqNo): 
+  AcsRequest(socket, address, empty, seqNo, vid, acs, lsm, panel, drive),
+  m_ctaConf(ctaConf),
+  m_acsDismountTape(vid, acs, lsm, panel, drive, m_acs, log, ctaConf),
+  m_lastTimeLibraryQueried(0),
+  m_log(log),
+  m_timeAcsCommandStarted(0) {  
+}
+
+//-----------------------------------------------------------------------------
+// tick 
+//-----------------------------------------------------------------------------
+void cta::mediachanger::acs::daemon::AcsRequestDismountTape::tick() {
+  try {
+    if (isToExecute()) {
+      m_log(LOG_DEBUG,"AcsRequestDismountTape::tick isToExecute");
+      m_acsDismountTape.asyncExecute(getSeqNo());
+      setStateIsRunning();  
+      m_timeAcsCommandStarted = time(0);
+    }  
+
+    const time_t now = time(0);
+    
+    const time_t secsSinceLastQuery = now -  m_lastTimeLibraryQueried;
+    const bool firstQueryOrTimeExceeded = (unsigned int)secsSinceLastQuery > 
+      m_ctaConf.QueryInterval.value();
+    
+    if(isRunning() && firstQueryOrTimeExceeded) {      
+      if(isResponseFinalAndSuccess()) {
+        setStateCompleted();
+        m_log(LOG_DEBUG,
+         "AcsRequestDismountTape::tick ACS_REQUEST_COMPLETED");
+      } else {
+        m_log(LOG_DEBUG,"AcsRequestDismountTape::tick "
+          "firstQueryOrTimeExceeded()");
+        m_lastTimeLibraryQueried = time(0);
+      }        
+    }
+    
+    const time_t secsSinceCommandStarted = now -  m_timeAcsCommandStarted;
+    const bool acsCommandTimeExceeded = (unsigned int)secsSinceCommandStarted >
+      m_ctaConf.CmdTimeout.value();
+    
+    if(isRunning() && acsCommandTimeExceeded) {
+      cta::exception::RequestFailed ex;
+      ex.getMessage() << "ACS command timed out after " << 
+        secsSinceCommandStarted << " seconds";
+      throw ex;
+    }
+  } catch (cta::exception::Exception &ex) {
+   setStateFailed(ECANCELED, ex.getMessage().str());
+    m_log(LOG_ERR,"Failed to handle the ACS dismount tape request: "
+      + ex.getMessage().str());
+  } catch(std::exception &se) {
+    setStateFailed(ECANCELED, se.what());
+    m_log(LOG_ERR, se.what());
+  } catch(...) {
+    setStateFailed(ECANCELED, "Caught an unknown exception");
+    m_log(LOG_ERR, "Caught an unknown exception");
+  }    
+}
+
+//------------------------------------------------------------------------------
+// isResponseFinalAndSuccess
+//------------------------------------------------------------------------------
+bool cta::mediachanger::acs::daemon::AcsRequestDismountTape::isResponseFinalAndSuccess() const  {
+  if (RT_FINAL == m_responseType ) {
+    const ACS_DISMOUNT_RESPONSE *const msg = 
+      (ACS_DISMOUNT_RESPONSE *)m_responseMsg;
+    if(STATUS_SUCCESS != msg->dismount_status) {
+      cta::exception::DismountFailed ex;
+      ex.getMessage() << "Status of dismount response is not success: " <<
+        acs_status(msg->dismount_status);
+      throw ex;
+    }    
+    return true;
+  }
+  return false;  
+}
diff --git a/mediachanger/acs/daemon/AcsRequestDismountTape.hpp b/mediachanger/acs/daemon/AcsRequestDismountTape.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ec284f6e473901e32fc4d555efdb94ca7c9cbf0
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsRequestDismountTape.hpp
@@ -0,0 +1,113 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/acs/daemon/AcsdConfiguration.hpp"
+#include "mediachanger/acs/daemon/AcsDismountTape.hpp"
+#include "mediachanger/acs/AcsImpl.hpp"
+#include "mediachanger/acs/daemon/AcsRequest.hpp"
+
+namespace cta     {
+namespace mediachanger     {
+namespace acs        {
+namespace daemon       {
+/**
+ * Concrete class providing a dismount tape implementation of an AcsRequest 
+ * abstract class.
+ */
+class AcsRequestDismountTape: public AcsRequest {
+public:
+
+  /**
+   * Constructor.
+   * 
+   * @param vid     The vid of the ACS request.
+   * @param acs     The acs value of the ACS request.
+   * @param lsm     The lsm value of the ACS request.
+   * @param panel   The panel value of the ACS request.
+   * @param drive   The drive value of the ACS request.
+   * @param ctaConf The configuration for the CTA ACS daemon.
+   * @param socket  ZMQ socket to use.
+   * @param address ZMQ message with client address.
+   * @param empty   ZMQ empty message.
+   * @param seqNo   Sequence number for the ACS request.
+   */
+  AcsRequestDismountTape(
+    const std::string &vid, 
+    const uint32_t acs,
+    const uint32_t lsm,
+    const uint32_t panel, 
+    const uint32_t drive,
+    const AcsdConfiguration &ctaConf,
+    mediachanger::ZmqSocketST &socket,
+    mediachanger::ZmqMsg &address,
+    mediachanger::ZmqMsg &empty,
+    cta::log::Logger& log,
+    const SEQ_NO seqNo);
+  
+  /**
+   * Perform any time related actions with the request to CTA ACS daemon.
+   *
+   * This method does not have to be called at any time precise interval.
+   */
+  void tick();
+ 
+  /**
+   * Checks the status of the response from the dismount response message buffer
+   * and the type of the response. Throws exception if the type of the response 
+   * is RT_FINAL but the status is not success.
+   * 
+   * @return true if the type of response RT_FINAL and the response status 
+   *         is STATUS_SUCCESS.
+   */
+  bool isResponseFinalAndSuccess() const;
+ 
+private:
+  
+  /**
+   * The CTA configuration parameters for the CTA ACS daemon.
+   */
+  const AcsdConfiguration m_ctaConf;
+  
+  /**
+   * The object representing the class for tape dismount through ACS API.
+   */
+  AcsDismountTape m_acsDismountTape;
+  
+  /**
+   * The ACLS C-API wrapper.
+   */  
+  AcsImpl m_acs;
+  
+  /**
+   * The time when the ACS library queried last time.
+   */  
+  time_t m_lastTimeLibraryQueried;
+  
+  cta::log::Logger &m_log;
+  /**
+   * The time at which ACS command was started.
+   */
+  time_t m_timeAcsCommandStarted;
+  
+}; // class AcsRequestDismountTape
+} // namespace daemon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/AcsdCmdLine.cpp b/mediachanger/acs/daemon/AcsdCmdLine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f60f567d3e9c7fb843a53e880b5ca78798435c0c
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsdCmdLine.cpp
@@ -0,0 +1,81 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AcsdCmdLine.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/log.hpp"
+#include <iostream>
+#include <getopt.h>
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsdCmdLine::AcsdCmdLine() throw():
+  foreground(false),
+  help(false),
+  configLocation(""){
+}
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsdCmdLine::AcsdCmdLine(const int argc,
+  char *const *const argv):
+  foreground(false),    //< Prevents daemonisation
+  help(false),          //< Help requested: will print out help and exit.
+  configLocation(""){   //< Location of the configuration file. Defaults to /etc/cta/cta-taped.conf
+
+  
+static struct option longopts[] = {
+    // { .name, .has_args, .flag, .val } (see getopt.h))
+    { "foreground", no_argument, NULL, 'f' },
+    { "config", required_argument, NULL, 'c' },
+    { "help", no_argument, NULL, 'h' },
+    { NULL, 0, NULL, '\0' }
+  };
+
+  char c;
+  // Reset getopt's global variables to make sure we start fresh
+  optind=0;
+  // Prevent getopt from printing out errors on stdout
+  opterr=0;
+  // We ask getopt to not reshuffle argv ('+')
+  while ((c = getopt_long(argc, argv, "+fc:h", longopts, NULL)) != -1) {
+     //log:write(LOG_INFO, "Usage: [options]\n");
+    switch (c) {
+    case 'f':
+      foreground = true;
+      break;
+    case 'c':
+      configLocation = optarg;
+      break;
+    case 'h':
+      help = true;
+   std::cout<<"Usage: "<<"cta-acsd"<<" [options]\n"
+    "where options can be:\n"
+    "--foreground            or -f         Remain in the Foreground\n"
+    "--config <config-file>  or -c         Configuration file\n"
+    "--help                  or -h         Print this help and exit\n";
+      break;
+    default:
+      break;
+    }
+  }
+}
+
+
diff --git a/mediachanger/acs/daemon/AcsdCmdLine.hpp b/mediachanger/acs/daemon/AcsdCmdLine.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..058a7d75f28c0fb0109420be23e76c87855f9d21
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsdCmdLine.hpp
@@ -0,0 +1,69 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mediachanger/CmdLine.hpp"
+#include <string.h>
+
+namespace cta{
+namespace mediachanger{
+namespace acs{
+namespace daemon{
+
+class AcsdCmdLine: public CmdLine{
+
+public:
+
+  /**
+   * Constructor.
+   *
+   * Initialises all BOOLEAN member-variables to FALSE, all integer
+   * member-variables to 0 and the volume identifier to an empty string.
+   */
+  AcsdCmdLine() throw();
+
+  /**
+   * Constructor.
+   *
+   * 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().
+   */
+  AcsdCmdLine(const int argc, char *const *const argv);
+
+   bool foreground;
+
+   bool help;
+ 
+   std::string configLocation;
+ 
+
+};
+
+}
+}
+}
+}
+
+
+
+
+
+
diff --git a/mediachanger/acs/daemon/AcsdCmdLineTest.cpp b/mediachanger/acs/daemon/AcsdCmdLineTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..385e997a00f3519e8b36e99e8cbdf955b94576b1
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsdCmdLineTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/exception/Exception.hpp"
+#include "mediachanger/acs/daemon/AcsdCmdLine.hpp"
+#include <gtest/gtest.h>
+#include <list>
+#include <memory>
+
+namespace unitTests {
+
+class cta_mediachanger_acs_daemon_AcsdCmdLineTest : public ::testing::Test {
+protected:
+
+  struct Argcv {
+    int argc;
+    char **argv;
+    Argcv(): argc(0), argv(NULL) {
+    }
+  };
+  typedef std::list<Argcv*> ArgcvList;
+  ArgcvList m_argsList;
+
+  /**
+   * Creates a duplicate string using 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;
+  }
+
+  virtual void SetUp() {
+  }
+
+  virtual 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;
+    }
+  }
+};
+
+
+TEST_F(cta_mediachanger_acs_daemon_AcsdCmdLineTest, constructor_no_command_line_args) {
+  using namespace cta::mediachanger::acs::daemon;
+
+  Argcv *args = new Argcv();
+  m_argsList.push_back(args);
+  args->argc = 1;
+  args->argv = new char *[2];
+  args->argv[0] = dupString("cta-acsd");
+  args->argv[1] = NULL;
+  cta::mediachanger::acs::daemon::AcsdCmdLine acsdcmdLine1(args->argc, args->argv);
+ 
+  ASSERT_FALSE(acsdcmdLine1.foreground);
+  ASSERT_TRUE(acsdcmdLine1.configLocation.empty());
+  ASSERT_FALSE(acsdcmdLine1.help);
+}
+
+TEST_F(cta_mediachanger_acs_daemon_AcsdCmdLineTest, default_constructor) {
+  using namespace cta::mediachanger::acs::daemon;
+
+  Argcv *args = new Argcv();
+  m_argsList.push_back(args);
+  args->argc = 0;
+  cta::mediachanger::acs::daemon::AcsdCmdLine acsdcmdLine1(args->argc, args->argv);
+ 
+  ASSERT_FALSE(acsdcmdLine1.foreground);
+  ASSERT_TRUE(acsdcmdLine1.configLocation.empty());
+  ASSERT_FALSE(acsdcmdLine1.help);
+}
+
+TEST_F(cta_mediachanger_acs_daemon_AcsdCmdLineTest, constructor_foreground) {
+  using namespace cta::mediachanger::acs::daemon;
+
+  Argcv *args = new Argcv();
+  m_argsList.push_back(args);
+  args->argc = 2;
+  args->argv = new char *[3];
+  args->argv[0] = dupString("cta-acsd");
+  args->argv[1] = dupString("-f");
+  args->argv[2] = NULL;
+  cta::mediachanger::acs::daemon::AcsdCmdLine acsdcmdLine1(args->argc, args->argv);
+ 
+  ASSERT_TRUE(acsdcmdLine1.foreground);
+}
+
+
+TEST_F(cta_mediachanger_acs_daemon_AcsdCmdLineTest, constructor_help) {
+  using namespace cta::mediachanger::acs::daemon;
+
+  Argcv *args = new Argcv();
+  m_argsList.push_back(args);
+  args->argc = 2;
+  args->argv = new char *[3];
+  args->argv[0] = dupString("cta-mediachanger-acs-daemon");
+  args->argv[1] = dupString("-h");
+  args->argv[2] = NULL;
+  cta::mediachanger::acs::daemon::AcsdCmdLine acsdcmdLine2(args->argc, args->argv);
+  ASSERT_TRUE(acsdcmdLine2.help);
+}
+
+ 
+TEST_F(cta_mediachanger_acs_daemon_AcsdCmdLineTest, constructor_configLocation) {
+  using namespace cta::mediachanger::acs::daemon;
+
+  Argcv *args1 = new Argcv();
+  m_argsList.push_back(args1);
+  args1->argc = 2;
+  args1->argv = new char *[3];
+  args1->argv[0] = dupString("cta-mediachanger-acs-daemon");
+  args1->argv[1] = dupString("-c");
+  args1->argv[2] = NULL;
+  cta::mediachanger::acs::daemon::AcsdCmdLine acsdcmdLine3(args1->argc, args1->argv);
+  ASSERT_EQ(acsdcmdLine3.configLocation,"");
+}
+
+} // namespace unitTests
diff --git a/mediachanger/acs/daemon/AcsdConfiguration.cpp b/mediachanger/acs/daemon/AcsdConfiguration.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5d5b42117a7a5d2f21db124c24d14aab5edd8c2
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsdConfiguration.cpp
@@ -0,0 +1,54 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AcsdConfiguration.hpp"
+#include "common/ConfigurationFile.hpp"
+#include "Tpconfig.hpp"
+#include <time.h>
+
+
+//------------------------------------------------------------------------------
+// GlobalConfiguration::createFromCtaConf w path
+//------------------------------------------------------------------------------
+cta::mediachanger::acs::daemon::AcsdConfiguration cta::mediachanger::acs::daemon::AcsdConfiguration::createFromCtaConf(
+  const std::string& generalConfigPath, cta::log::Logger& log) {
+  AcsdConfiguration ret;
+  // Parse config file
+  ConfigurationFile cf(generalConfigPath);
+  // Extract configuration from parsed config file TpConfig
+  ret.port.setFromConfigurationFile(cf, generalConfigPath);
+  ret.QueryInterval.setFromConfigurationFile(cf, generalConfigPath);
+  ret.CmdTimeout.setFromConfigurationFile(cf, generalConfigPath);
+  ret.daemonUserName.setFromConfigurationFile(cf, generalConfigPath);
+  ret.daemonGroupName.setFromConfigurationFile(cf, generalConfigPath);
+
+  // If we get here, the configuration file is good enough to be logged.
+  ret.port.log(log);
+  ret.QueryInterval.log(log);
+  ret.CmdTimeout.log(log);
+  ret.daemonUserName.log(log);
+  ret.daemonGroupName.log(log);
+  
+  return ret;
+}
+
+//------------------------------------------------------------------------------
+// GlobalConfiguration::gDummyLogger (static member)
+//------------------------------------------------------------------------------
+cta::log::DummyLogger cta::mediachanger::acs::daemon::AcsdConfiguration::gDummyLogger("","");
+
diff --git a/mediachanger/acs/daemon/AcsdConfiguration.hpp b/mediachanger/acs/daemon/AcsdConfiguration.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..61f70f714e299bd7ee059aef75e11e454a5b3233
--- /dev/null
+++ b/mediachanger/acs/daemon/AcsdConfiguration.hpp
@@ -0,0 +1,64 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <string>
+#include <map>
+#include <type_traits>
+#include <limits>
+#include "common/log/DummyLogger.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/SourcedParameter.hpp"
+#include "Tpconfig.hpp"
+#include "mediachanger/acs/Constants.hpp"
+#include "common/ConfigurationFile.hpp"
+
+#include <time.h>
+
+namespace cta {
+namespace mediachanger { 
+namespace acs	{
+namespace daemon {
+/**
+ * Class containing all the parameters needed by the watchdog process
+ * to spawn a transfer session per drive.
+ */
+struct AcsdConfiguration {
+  static AcsdConfiguration createFromCtaConf(
+          const std::string & generalConfigPath,
+          cta::log::Logger & log = gDummyLogger);
+ 
+  SourcedParameter<uint64_t> port{
+    "acsd", "Port", (uint64_t)ACS_PORT, "Compile time default"};
+  SourcedParameter<uint64_t> QueryInterval{ 
+    "acsd", "QueryInterval", (long unsigned int)ACS_QUERY_INTERVAL, "Compile time default"};
+  SourcedParameter<uint64_t> CmdTimeout{
+    "acsd", "CmdTimeout",(long unsigned int) ACS_CMD_TIMEOUT, "Compile time default"};
+  SourcedParameter<std::string> daemonUserName{
+    "acsd", "DaemonUserName", "cta", "Compile time default"};
+  SourcedParameter<std::string> daemonGroupName{
+    "acsd", "DaemonGroupName", "tape", "Compile time default"};
+private:
+  /** A private dummy logger which will simplify the implementation of the 
+   * functions (just unconditionally log things). */
+  static cta::log::DummyLogger gDummyLogger;
+} ;
+} // namespace daemon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/CMakeLists.txt b/mediachanger/acs/daemon/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1dfee71cbc555055c2cd396987b554543971b81f
--- /dev/null
+++ b/mediachanger/acs/daemon/CMakeLists.txt
@@ -0,0 +1,63 @@
+cmake_minimum_required (VERSION 2.6)
+
+find_package(Protobuf3 REQUIRED)
+find_package(openssl REQUIRED)
+find_package(zeromq REQUIRED)
+
+include_directories (${PROTOBUF3_INCLUDE_DIRS})
+
+
+PROTOBUF3_GENERATE_CPP(WDMsgSources WDMsgHeaders WatchdogMessage.proto)
+
+SET_SOURCE_FILES_PROPERTIES(${WDMsgHeaders} PROPERTIES HEADER_FILE_ONLY TRUE)
+
+
+set (ACS_DAEMON_SRC_FILES
+  AcsDaemon.cpp
+  AcsDaemonMain.cpp
+  AcsdConfiguration.cpp
+  AcsRequest.cpp
+  AcsPendingRequests.cpp
+  AcsdCmdLine.cpp
+  AcsDismountTape.cpp
+  AcsMessageHandler.cpp
+  AcsMountTapeReadWrite.cpp 
+  AcsMountTapeReadOnly.cpp
+  AcsForceDismountTape.cpp
+  Constants.cpp
+  AcsRequestDismountTape.cpp
+)
+add_executable (cta-acsd ${ACS_DAEMON_SRC_FILES})
+
+set_target_properties (cta-acsd PROPERTIES
+  COMPILE_FLAGS -I/usr/include/CDK
+  COMPILE_DEFINITIONS LINUX)
+
+target_link_libraries(
+  cta-acsd
+  ctacommon
+  ctareactor 
+  cta-acs
+  ctamediachanger 
+  zmq
+  ${STK_LIBRARIES})
+
+set_property (TARGET cta-acsd APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
+
+install (FILES cta-acsd.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cta)
+install (TARGETS cta-acsd DESTINATION /usr/bin)
+install (FILES cta-acsd.logrotate DESTINATION /etc/logrotate.d RENAME cta-acsd)
+install (FILES cta-acsd.sysconfig DESTINATION /etc/sysconfig RENAME cta-acsd)
+install (FILES cta-acsd.service DESTINATION /etc/systemd/system)
+
+add_library (ctamediachangeracsdaemonunittests SHARED AcsdCmdLineTest.cpp AcsdCmdLine.cpp)
+set_property(TARGET ctamediachangeracsdaemonunittests PROPERTY SOVERSION "${CTA_SOVERSION}")
+set_property(TARGET ctamediachangeracsdaemonunittests PROPERTY   VERSION "${CTA_LIBVERSION}")
+
+target_link_libraries (ctamediachangeracsdaemonunittests
+ ctamediachangerunittests)
+
+install (TARGETS ctamediachangeracsdaemonunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
+
+
+
diff --git a/mediachanger/acs/daemon/Constants.cpp b/mediachanger/acs/daemon/Constants.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b2feef15fe8e8bd53c66f0f03e1eb84962dfc43
--- /dev/null
+++ b/mediachanger/acs/daemon/Constants.cpp
@@ -0,0 +1,79 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Constants.hpp"
+
+//------------------------------------------------------------------------------
+// msgTypeToString
+//------------------------------------------------------------------------------
+const char *cta::mediachanger::acs::daemon::msgTypeToString(const cta::mediachanger::acs::daemon::MsgType msgType) throw() {
+  switch(msgType) {
+  case MSG_TYPE_NONE:
+    return "None";
+  case MSG_TYPE_EXCEPTION:
+    return "Exception";
+  case MSG_TYPE_FORKCLEANER:
+    return "ForkCleaner";
+  case MSG_TYPE_FORKDATATRANSFER:
+    return "ForkDataTransfer";
+  case MSG_TYPE_FORKLABEL:
+    return "ForkLabel";
+  case MSG_TYPE_FORKSUCCEEDED:
+    return "ForkSucceeded";
+  case MSG_TYPE_HEARTBEAT:
+    return "Heartbeat";
+  case MSG_TYPE_MIGRATIONJOBFROMTAPEGATEWAY:
+    return "MigrationJobFromTapeGateway";
+  case MSG_TYPE_MIGRATIONJOBFROMWRITETP:
+    return "MigrationJobFromWriteTp";
+  case MSG_TYPE_NBFILESONTAPE:
+    return "NbFilesOnTape";
+  case MSG_TYPE_PROCESSCRASHED:
+    return "ProcessCrashed";
+  case MSG_TYPE_PROCESSEXITED:
+    return "ProcessExited";
+  case MSG_TYPE_RECALLJOBFROMREADTP:
+    return "RecallJobFromReadTp";
+  case MSG_TYPE_RECALLJOBFROMTAPEGATEWAY:
+    return "RecallJobFromTapeGAteway";
+  case MSG_TYPE_RETURNVALUE:
+    return "ReturnValue";
+  case MSG_TYPE_STOPPROCESSFORKER:
+    return "StopProcessForker";
+  case MSG_TYPE_TAPEMOUNTEDFORMIGRATION:
+    return "TapeMountedForMigration";
+  case MSG_TYPE_TAPEMOUNTEDFORRECALL:
+    return "TapeMountedForRecall";
+  case MSG_TYPE_LABELERROR:
+    return "LabelError";
+  case MSG_TYPE_ACSMOUNTTAPEREADONLY:
+    return "AcsMountTapeReadOnly";
+  case MSG_TYPE_ACSMOUNTTAPEREADWRITE:
+    return "AcsMountTapeReadWrite";        
+  case MSG_TYPE_ACSDISMOUNTTAPE:
+    return "AcsDismountTape";
+  case MSG_TYPE_ACSFORCEDISMOUNTTAPE:
+    return "AcsForceDismountTape";
+  case MSG_TYPE_ADDLOGPARAMS:
+    return "AddLogParams";
+  case MSG_TYPE_DELETELOGPARAMS:
+    return "DeleteLogParams";
+  default:
+    return "Unknown";
+  }
+} // msgTypeToString()
diff --git a/mediachanger/acs/daemon/Constants.hpp b/mediachanger/acs/daemon/Constants.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..08098228e3746563e503a373cfbbe2a683345cef
--- /dev/null
+++ b/mediachanger/acs/daemon/Constants.hpp
@@ -0,0 +1,83 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+namespace cta {
+namespace mediachanger {
+namespace acs {
+namespace daemon {
+
+enum ProtocolType {
+  PROTOCOL_TYPE_NONE,
+  PROTOCOL_TYPE_TAPE
+};
+
+enum MsgType {
+  /*  0 */ MSG_TYPE_NONE                        =  0,
+  /*  1 */ MSG_TYPE_EXCEPTION                   =  1,
+  /*  2 */ MSG_TYPE_FORKCLEANER                 =  2,
+  /*  3 */ MSG_TYPE_FORKDATATRANSFER            =  3,
+  /*  4 */ MSG_TYPE_FORKLABEL                   =  4,
+  /*  5 */ MSG_TYPE_FORKSUCCEEDED               =  5,
+  /*  6 */ MSG_TYPE_HEARTBEAT                   =  6,
+  /*  7 */ MSG_TYPE_MIGRATIONJOBFROMTAPEGATEWAY =  7,
+  /*  8 */ MSG_TYPE_MIGRATIONJOBFROMWRITETP     =  8,
+  /*  9 */ MSG_TYPE_NBFILESONTAPE               =  9,
+  /* 10 */ MSG_TYPE_PROCESSCRASHED              = 10,
+  /* 11 */ MSG_TYPE_PROCESSEXITED               = 11,
+  /* 12 */ MSG_TYPE_RECALLJOBFROMREADTP         = 12,
+  /* 13 */ MSG_TYPE_RECALLJOBFROMTAPEGATEWAY    = 13,
+  /* 14 */ MSG_TYPE_RETURNVALUE                 = 14,
+  /* 15 */ MSG_TYPE_STOPPROCESSFORKER           = 15,
+  /* 16 */ MSG_TYPE_TAPEMOUNTEDFORMIGRATION     = 16,
+  /* 17 */ MSG_TYPE_TAPEMOUNTEDFORRECALL        = 17,
+  /* 18 */ MSG_TYPE_TAPEUNMOUNTSTARTED          = 18,
+  /* 19 */ MSG_TYPE_TAPEUNMOUNTED               = 19,
+  /* 20 */ MSG_TYPE_LABELERROR                  = 20,
+  /* 21 */ MSG_TYPE_ACSMOUNTTAPEREADONLY        = 21,
+  /* 22 */ MSG_TYPE_ACSMOUNTTAPEREADWRITE       = 22,
+  /* 23 */ MSG_TYPE_ACSDISMOUNTTAPE             = 23,
+  /* 24 */ MSG_TYPE_ACSFORCEDISMOUNTTAPE        = 24,
+  /* 25 */ MSG_TYPE_ADDLOGPARAMS                = 25,
+  /* 26 */ MSG_TYPE_DELETELOGPARAMS             = 26,
+  /* 27 */ MSG_TYPE_ARCHIVEJOBFROMCTA           = 27,
+  /* 28 */ MSG_TYPE_RETRIEVEJOBFROMCTA          = 28
+};
+
+enum ProtocolVersion {
+  PROTOCOL_VERSION_NONE,
+  PROTOCOL_VERSION_1
+};
+
+/**
+ * Returns the string representation of the specified message type.
+ *
+ * This method is thread safe because it only returns pointers to string
+ * literals.
+ *
+ * In the case where the specified message type is unknown this method does not
+ * throw an exception, instead is returns a string literal that explains the
+ * message type is unknown.
+ */
+const char *msgTypeToString(const MsgType msgType) throw();
+
+} // namespace daemon
+} // namespace acs
+} // namespace mediachanger
+} // namespace cta
diff --git a/mediachanger/acs/daemon/Tpconfig.hpp b/mediachanger/acs/daemon/Tpconfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..47fafa56aa2fcc65e5aaf84a121c5e76d3db4a21
--- /dev/null
+++ b/mediachanger/acs/daemon/Tpconfig.hpp
@@ -0,0 +1,46 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "TpconfigLine.hpp"
+#include "common/SourcedParameter.hpp"
+#include "common/exception/Exception.hpp"
+
+#include <map>
+
+namespace cta { namespace mediachanger { namespace acs { namespace daemon {
+
+/**
+ * A map of lines parsed from a TPCONFIG file (key is the drive name)
+ */
+class Tpconfig: public std::map<std::string, SourcedParameter<cta::mediachanger::acs::daemon::TpconfigLine>> {
+public:
+
+  CTA_GENERATE_EXCEPTION_CLASS(InvalidArgument);
+  CTA_GENERATE_EXCEPTION_CLASS(DuplicateEntry);
+  /**
+   * Parses the specified TPCONFIG file.
+   *
+   * @param filename The filename of the TPCONFIG file.
+   * @return The result of parsing the TPCONFIG file.
+   */
+  static Tpconfig parseFile(const std::string &filename);
+}; // class TpconfigLines
+
+}}}} // namespace cta::tape::daemon
diff --git a/mediachanger/acs/daemon/TpconfigLine.hpp b/mediachanger/acs/daemon/TpconfigLine.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4b7a74b4cbd9bc969d1acb415b86237808c01cdb
--- /dev/null
+++ b/mediachanger/acs/daemon/TpconfigLine.hpp
@@ -0,0 +1,104 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <string>
+#include "mediachanger/LibrarySlot.hpp"
+#include <memory>
+
+namespace cta {
+namespace mediachanger {
+namespace acs {
+namespace daemon {
+
+/**
+ * The data stored in a data-line (as opposed to a comment-line) from a
+ * TPCONFIG file (/etc/cta/TPCONFIG).
+ */
+class TpconfigLine {
+public:
+  /**
+   * The unit name of the tape drive.
+   */
+  std::string unitName;
+
+  /**
+   * The logical library of the tape drive.
+   */
+  std::string logicalLibrary;
+
+  /**
+   * The filename of the device file of the tape drive.
+   */
+  std::string devFilename;
+
+  /**
+   * The slot in the tape library that contains the tape drive (string encoded).
+   */
+  std::string rawLibrarySlot;
+  
+  /**
+   * Accessor method to the library slot strcuture.
+   * @return reference to the library slot.
+   */
+  const cta::mediachanger::LibrarySlot & librarySlot() const;
+  
+private:
+  /**
+   * The library slot structure.
+   */
+  std::unique_ptr <cta::mediachanger::LibrarySlot> m_librarySlot;
+
+public:
+  /**
+   * Trivial constructor (used in unit tests).
+   */
+  TpconfigLine();
+  
+  /**
+   * Constructor.
+   *
+   * @param unitName The unit name of the tape drive.
+   * @param dgn The Device Group Name (DGN) of the tape drive.
+   * @param devFilename The filename of the device file of the tape drive.
+   * @param librarySlot The slot in the tape library that contains the tape
+   * drive.
+   */
+  TpconfigLine(
+    const std::string &unitName,
+    const std::string &logicalLibrary,
+    const std::string &devFilename,
+    const std::string &librarySlot);
+
+  /**
+   * Copy constructor
+   * @param o the other TpConfigLine to be copied.
+   */
+  TpconfigLine(const TpconfigLine& o);
+  
+  /**
+   * Copy operator
+   * @param o the other TpConfigLine to copy.
+   * @return a reference to the object
+   */
+  TpconfigLine& operator=(const TpconfigLine& o);
+  static const size_t maxNameLen = 100;
+}; // struct TpconfigLine
+
+}}}} // namespace cta::mediachanger::acs::daemon
diff --git a/mediachanger/acs/daemon/cta-acsd.conf b/mediachanger/acs/daemon/cta-acsd.conf
new file mode 100644
index 0000000000000000000000000000000000000000..e97d8b4096ae2f3df65004ff221f7b5043042c5c
--- /dev/null
+++ b/mediachanger/acs/daemon/cta-acsd.conf
@@ -0,0 +1,38 @@
+# The CERN Tape Archive (CTA) project
+# Copyright (C) 2015  CERN
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+################################################################################
+#
+# CTA Sample Configuration File
+#
+################################################################################
+
+# The log mask.  Logs with a level lower than this value will be masked.
+# Possible values are:
+#   EMERG
+#   ALERT
+#   CRIT
+#   ERR
+#   WARNING
+#   NOTICE
+#   INFO
+#   DEBUG
+#   USERERR
+#
+# Please note that the USERERR log level is equivalent to NOTICE because it is
+# a convention of CTA to use log level NOTICE to label user errors.
+#
+# taped LogMask INFO
diff --git a/mediachanger/acs/daemon/cta-acsd.logrotate b/mediachanger/acs/daemon/cta-acsd.logrotate
new file mode 100644
index 0000000000000000000000000000000000000000..de8fd8ac53024552e0a710d8d6f87ba5a832de3f
--- /dev/null
+++ b/mediachanger/acs/daemon/cta-acsd.logrotate
@@ -0,0 +1,10 @@
+/var/log/cta/cta-acsd*.log {
+    compress
+    daily
+    missingok
+    rotate 500
+    delaycompress
+    postrotate
+        /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
diff --git a/mediachanger/acs/daemon/cta-acsd.service b/mediachanger/acs/daemon/cta-acsd.service
new file mode 100644
index 0000000000000000000000000000000000000000..b6c4c395248e37320ddcd74d158645c546e3810e
--- /dev/null
+++ b/mediachanger/acs/daemon/cta-acsd.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=CERN Tape Archive (CTA) acsd daemon
+After=syslog.target network-online.target
+
+[Service]
+EnvironmentFile=-/etc/sysconfig/cta-acsd
+ExecStart=/usr/bin/cta-acsd ${CTA_ACSD_OPTIONS}
+LimitCORE=infinity
+Type=forking
+Restart=no
+
+[Install]
+WantedBy=default.target
diff --git a/mediachanger/acs/daemon/cta-acsd.sysconfig b/mediachanger/acs/daemon/cta-acsd.sysconfig
new file mode 100644
index 0000000000000000000000000000000000000000..8131adf1c7a76484dc10974d51bbd8a8a93b349f
--- /dev/null
+++ b/mediachanger/acs/daemon/cta-acsd.sysconfig
@@ -0,0 +1,5 @@
+
+# Arguments for the rmcd server daemon
+#   -smc_ldr       is the picker device as defined in /dev.
+#   -f             keep process in the foreground, do not fork.
+#CTA_ACSD_OPTIONS="/dev/sg9"
diff --git a/mediachanger/reactor/CMakeLists.txt b/mediachanger/reactor/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9f2886542aab6e83b0124d7db5ba7e5d7920f197
--- /dev/null
+++ b/mediachanger/reactor/CMakeLists.txt
@@ -0,0 +1,39 @@
+# This file is part of the Castor project.
+# See http://castor.web.cern.ch/castor
+#
+# Copyright (C) 2003  CERN
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+# @author Castor Dev team, castor-dev@cern.ch
+#
+cmake_minimum_required (VERSION 2.6)
+
+################################################################################
+# Rules to build the reactor code that is common to both rmcd and tapeserverd
+################################################################################
+set (REACTOR_SRC_FILES
+ # DummyZMQReactor.cpp
+ # PollEventHandler.cpp
+ # PollReactor.cpp
+ # PollReactorImpl.cpp
+  ZMQPollEventHandler.cpp
+  ZMQReactor.cpp)
+add_library (ctareactor ${REACTOR_SRC_FILES})
+set_target_properties (ctareactor PROPERTIES
+  COMPILE_FLAGS -I/usr/include/CDK
+  COMPILE_DEFINITIONS LINUX)
+target_link_libraries (ctareactor ctacommon ctamediachanger zmq
+  ${STK_LIBRARIES})
+#target_link_libraries (ctareactor ctacommon ctamediachanger)
+
diff --git a/mediachanger/reactor/ZMQPollEventHandler.cpp b/mediachanger/reactor/ZMQPollEventHandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..858a8859dad12deb00549480d3cf1e4276067d8c
--- /dev/null
+++ b/mediachanger/reactor/ZMQPollEventHandler.cpp
@@ -0,0 +1,28 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#include "ZMQPollEventHandler.hpp"
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::reactor::ZMQPollEventHandler::~ZMQPollEventHandler() throw() {
+}
diff --git a/mediachanger/reactor/ZMQPollEventHandler.hpp b/mediachanger/reactor/ZMQPollEventHandler.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f43df3d4813be1716d196f2a0c5ec49dc84c81c
--- /dev/null
+++ b/mediachanger/reactor/ZMQPollEventHandler.hpp
@@ -0,0 +1,77 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+#include "common/exception/Exception.hpp"
+
+#include <zmq.h>
+
+namespace cta {
+namespace mediachanger {
+namespace reactor {
+
+/**
+ * Handles the events that occur on a poll() file descriptor.
+ *
+ * This class is part of an implementation of the Reactor architecture pattern
+ * described in the following book:
+ *
+ *    Pattern-Oriented Software Architecture Volume 2
+ *    Patterns for Concurrent and Networked Objects
+ *    Authors: Schmidt, Stal, Rohnert and Buschmann
+ *    Publication date: 2000
+ *    ISBN 0-471-60695-2
+ */
+class ZMQPollEventHandler {
+public:
+
+  /**
+   * Destructor.
+   */
+  virtual ~ZMQPollEventHandler() throw() = 0;
+
+  /**
+   * Returns the human-readable name this event handler.
+   */
+  virtual std::string getName() const throw() = 0;
+
+  /**
+   * Fills the specified poll file-descriptor ready to be used in a call to
+   * poll().
+   */
+  virtual void fillPollFd(zmq_pollitem_t &pollitem) =0;
+
+  /**
+   * Handles the specified event.
+   *
+   * @param fd The poll file-descriptor describing the event.
+   * @return true if the event handler should be removed from and deleted by
+   * the reactor.
+   */
+  virtual bool handleEvent(const zmq_pollitem_t &fd)=0;
+
+}; // class ZMQPollEventHandler
+
+} // namespace reactor
+} // namespace tape
+} // namespace cta
+
diff --git a/mediachanger/reactor/ZMQReactor.cpp b/mediachanger/reactor/ZMQReactor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef3d4e3e41434a0d69a08a72df8aae0e6f281a0e
--- /dev/null
+++ b/mediachanger/reactor/ZMQReactor.cpp
@@ -0,0 +1,179 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#include "common/log/log.hpp"
+#include "ZMQReactor.hpp"
+#include "ZMQPollEventHandler.hpp"
+#include "common/utils/utils.hpp"
+
+#include "common/log/SyslogLogger.hpp"
+#include <algorithm>
+
+namespace{
+  bool operator==(const zmq_pollitem_t& a,const zmq_pollitem_t& b){
+       if( (a.fd==b.fd && a.fd!= -1 && b.fd != -1) || 
+            (a.socket==b.socket && a.socket!=NULL && b.socket != NULL) ){
+      return true;
+    }
+    return false;
+  }
+}
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::mediachanger::reactor::ZMQReactor::ZMQReactor(cta::log::Logger &l) throw():m_log(l) {
+}
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+cta::mediachanger::reactor::ZMQReactor::~ZMQReactor() throw() {
+  clear();
+}
+
+//------------------------------------------------------------------------------
+// clear
+//------------------------------------------------------------------------------
+void cta::mediachanger::reactor::ZMQReactor::clear() {
+  for(HandlerMap::iterator it=m_handlers.begin();it!=m_handlers.end();++it){
+    delete it->second;
+  }
+  m_handlers.clear();
+}
+
+
+//------------------------------------------------------------------------------
+// registerHandler
+//------------------------------------------------------------------------------
+
+  void cta::mediachanger::reactor::ZMQReactor::registerHandler(
+  ZMQPollEventHandler *const handler) {
+  zmq_pollitem_t item;
+  handler->fillPollFd(item);
+
+  std::ostringstream socketInHex;
+  socketInHex << std::hex << item.socket;
+  std::list<log::Param> params = {log::Param("fd", item.fd),
+    log::Param("socket", socketInHex.str())};
+  //log::write(LOG_DEBUG, "ZMQReactor registering a new handler", params);
+  m_log(LOG_DEBUG, "ZMQReactor registering a new handler", params);
+
+  checkDoubleRegistration(item);
+  m_handlers.push_back(std::make_pair(item,handler));
+}
+
+//------------------------------------------------------------------------------
+// checkDoubleRegistration
+//------------------------------------------------------------------------------
+void cta::mediachanger::reactor::ZMQReactor::checkDoubleRegistration(
+  const zmq_pollitem_t &item) const {
+  for(HandlerMap::const_iterator it=m_handlers.begin(); it!=m_handlers.end();
+    ++it) {
+    const std::pair<zmq_pollitem_t, ZMQPollEventHandler*> &maplet = *it;
+    if(item == maplet.first) {
+      cta::exception::Exception ex;
+      ex.getMessage() << "ZMQReactor detected a double registration: fd=" <<
+        item.fd << " socket=" << std::hex << item.socket;
+      throw ex;
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleEvents
+//------------------------------------------------------------------------------
+void cta::mediachanger::reactor::ZMQReactor::handleEvents(const int timeout) {
+  //it should not bring any copy, thanks to NRVO
+  std::vector<zmq_pollitem_t> pollFds=buildPollFds();
+
+  // Please note that we are relying on the fact that the file descriptors of
+  // the vector are stored contiguously
+  const int pollRc = zmq_poll(&pollFds[0], pollFds.size(), timeout);  
+  if(0 <= pollRc){
+    for(std::vector<zmq_pollitem_t>::const_iterator it=pollFds.begin();
+      it!=pollFds.end(); ++it) {
+      const zmq_pollitem_t &pollFd = *it;
+      if(0 != pollFd.revents) {
+        handleEvent(pollFd);
+      }
+    }
+  } else if(0 > pollRc) {
+    const std::string message = utils::errnoToString(errno);
+    std::list<log::Param> params = {log::Param("message", message)};
+    m_log(LOG_ERR, "Failed to handle I/O event: zmq_poll() failed", params);
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleEvent
+//------------------------------------------------------------------------------
+void cta::mediachanger::reactor::ZMQReactor::handleEvent(
+  const zmq_pollitem_t &pollFd) {
+  // Find and dispatch the appropriate handler if there is a pending event
+  if(pollFd.revents & ZMQ_POLLIN) {
+    HandlerMap::iterator handlerItor = findHandler(pollFd);
+    if(handlerItor != m_handlers.end()) {
+      ZMQPollEventHandler *const handler = handlerItor->second;
+      const bool removeAndDeleteHandler = handler->handleEvent(pollFd);
+      if(removeAndDeleteHandler) { 
+        m_handlers.erase(handlerItor);
+        delete(handler);
+      }
+    }else {
+      std::list<log::Param> params;
+      params.push_back(log::Param("fd",pollFd.fd));
+      params.push_back(log::Param("socket",pollFd.socket));
+      m_log(LOG_ERR, "Event on some poll, but no handler to match it", params);
+    }
+  }
+}
+  
+//------------------------------------------------------------------------------
+// findHandler
+//------------------------------------------------------------------------------
+
+cta::mediachanger::reactor::ZMQReactor::HandlerMap::iterator
+  cta::mediachanger::reactor::ZMQReactor::findHandler(const zmq_pollitem_t& pollfd) {
+  for(HandlerMap::iterator it=m_handlers.begin();it!=m_handlers.end();++it){
+    // Use overloaded == to compare zmq_pollitem_t references
+    if(pollfd==it->first){
+      return it;
+    } 
+  }
+  return m_handlers.end();
+}
+
+//------------------------------------------------------------------------------
+// buildPollFds
+//------------------------------------------------------------------------------
+std::vector<zmq_pollitem_t> cta::mediachanger::reactor::ZMQReactor::buildPollFds()
+  const {
+  std::vector<zmq_pollitem_t> pollFds;
+  pollFds.reserve(m_handlers.size());
+  for(HandlerMap::const_iterator it=m_handlers.begin();it!=m_handlers.end();
+    ++it) {
+    ZMQPollEventHandler *const handler = it->second;
+    zmq_pollitem_t pollFd;
+    handler->fillPollFd(pollFd);
+    pollFds.push_back(pollFd);
+  }
+  return pollFds;
+}
diff --git a/mediachanger/reactor/ZMQReactor.hpp b/mediachanger/reactor/ZMQReactor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2cf4d62652ba1981f27a0d2e3f91621bcc60990
--- /dev/null
+++ b/mediachanger/reactor/ZMQReactor.hpp
@@ -0,0 +1,141 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+#include "ZMQPollEventHandler.hpp"
+#include "ZMQReactor.hpp"
+
+#include "common/log/log.hpp"
+#include <utility>
+#include <vector>
+
+namespace cta {
+namespace mediachanger {
+namespace reactor {
+
+/**
+ * This reactor wraps the zmq_poll() function.
+ *
+ * This class is part of an implementation of the Reactor architecture pattern
+ * described in the following book:
+ *
+ *    Pattern-Oriented Software Architecture Volume 2
+ *    Patterns for Concurrent and Networked Objects
+ *    Authors: Schmidt, Stal, Rohnert and Buschmann
+ *    Publication date: 2000
+ *    ISBN 0-471-60695-2
+ */
+class ZMQReactor {
+public:
+
+  /**
+   * Constructor.
+   */
+  ZMQReactor(cta::log::Logger &) throw();
+  /**
+   * Destructor.
+   */
+  virtual ~ZMQReactor() throw();
+
+  /**
+   * Removes and deletes all of the event handlers registered with the reactor.
+   */
+ virtual void clear();
+
+  /**
+   * Registers the specified handler.
+   *
+   * Please note that the reactor takes ownership of the handler and will
+   * delete it as appropriate.
+   *
+   * @param handler The handler to be registered.  Please note that the handler
+   * MUST be allocated on the heap because the reactor will own the handler
+   * and therefore delete it as needed.
+   */
+  virtual void registerHandler(ZMQPollEventHandler *const handler);
+
+  /**
+   * Handles any pending events.
+   *
+   * @param timeout Timeout in milliseconds.
+   */
+  virtual void handleEvents(const int timeout);
+  
+private:
+
+  log::Logger &m_log;
+  
+  /**
+   * Type used to map zmq_pollitem_t to event handler.
+   */
+typedef std::vector<std::pair<zmq_pollitem_t, ZMQPollEventHandler*> >
+    HandlerMap;
+
+  /**
+   * Throws a cator::exception::Exception if a handler has already been
+   * registered for the specified poll item.
+   *
+   * @prama item The poll item.
+   */
+  void checkDoubleRegistration(const zmq_pollitem_t &item) const;
+
+  /**
+   * Builds the vector of file descriptors to be passed to poll().
+   *
+   * Please note that the return type is an std::vector because we can assume
+   * that its elements are stored contiguously in memory.  The address of the
+   * first element is going to be passed to zmq_poll().
+   *
+   * @return The vector of file descriptors.
+   */
+  std::vector<zmq_pollitem_t> buildPollFds() const;
+  
+  /**
+   * Returns the event handler associated with the specified zmq_pollitem_t.
+   */
+  HandlerMap::iterator findHandler(const zmq_pollitem_t&);
+  
+  /**
+   * Handles the specified ZMQ I/O event.
+   *
+   * @param pollFd The file-descriptor representing the I/O event.
+   */
+  void handleEvent(const zmq_pollitem_t &pollFd);
+  
+  /**
+   * Removes the specified handler from the reactor.  This method effectively
+   * does the opposite of registerHandler().
+   *
+   * @param handler The handler to be removed.
+   */
+  void removeHandler(ZMQPollEventHandler *const handler);
+  
+  /**
+   * Map of file descriptor to registered event handler.
+   */
+  HandlerMap m_handlers;  
+  
+}; // class ZMQReactor
+
+} // namespace reactor
+} // namespace mediachanger
+} // namespace cta
diff --git a/objectstore/AgentHeartbeatThread.cpp b/objectstore/AgentHeartbeatThread.cpp
index d343d747720df96f07223f9ba6410d33d61f5faa..b0a5d1e15ac013af1d7944657b0c75274277b51c 100644
--- a/objectstore/AgentHeartbeatThread.cpp
+++ b/objectstore/AgentHeartbeatThread.cpp
@@ -18,6 +18,7 @@
 
 #include "AgentHeartbeatThread.hpp"
 #include "common/log/LogContext.hpp"
+#include "common/Timer.hpp"
 
 namespace cta { namespace objectstore {
 
@@ -38,7 +39,17 @@ void AgentHeartbeatThread::run() {
   auto exitFuture = m_exit.get_future();
   try {
     while (std::future_status::ready != exitFuture.wait_for(m_heartRate)) {
+      utils::Timer t;
       m_agentReference.bumpHeatbeat(m_backend);
+      auto updateTime = t.secs();
+      auto updateTimeLimit = std::chrono::duration<double, std::ratio<1, 1>>(m_heartRate).count();
+      if (updateTime > updateTimeLimit) {
+        log::ScopedParamContainer params(lc);
+        params.add("HeartbeatUpdateTimeLimit", updateTimeLimit)
+              .add("HeartbeatUpdateTime", updateTime);
+        lc.log(log::CRIT, "In AgentHeartbeatThread::run(): Could not update heartbeat in time. Exiting.");
+        ::exit(EXIT_FAILURE);
+      }
     }
     ANNOTATE_HAPPENS_AFTER(&m_exit);
     ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(&m_exit);
diff --git a/objectstore/GarbageCollectorTest.cpp b/objectstore/GarbageCollectorTest.cpp
index 24f2dd93a745fc4cd13eb142cec43eb941546318..8079b3e18337ce9124e121474dc6598edad72116 100644
--- a/objectstore/GarbageCollectorTest.cpp
+++ b/objectstore/GarbageCollectorTest.cpp
@@ -43,7 +43,11 @@ namespace unitTests {
 
 TEST(ObjectStore, GarbageCollectorBasicFuctionnality) {
   // We will need a log object 
+#ifdef STDOUT_LOGGING
+  cta::log::StdoutLogger dl("dummy", "unitTest");
+#else
   cta::log::DummyLogger dl("dummy", "unitTest");
+#endif
   cta::catalogue::DummyCatalogue catalogue;
   cta::log::LogContext lc(dl);
   // Here we check for the ability to detect dead (but empty agents)
@@ -96,7 +100,11 @@ TEST(ObjectStore, GarbageCollectorBasicFuctionnality) {
 
 TEST(ObjectStore, GarbageCollectorRegister) {
   // We will need a log object 
+#ifdef STDOUT_LOGGING
+  cta::log::StdoutLogger dl("dummy", "unitTest");
+#else
   cta::log::DummyLogger dl("dummy", "unitTest");
+#endif
   cta::log::LogContext lc(dl);
   cta::catalogue::DummyCatalogue catalogue;
   // Here we check that can successfully call agentRegister's garbage collector
@@ -155,7 +163,11 @@ TEST(ObjectStore, GarbageCollectorRegister) {
 
 TEST(ObjectStore, GarbageCollectorArchiveQueue) {
   // We will need a log object 
+#ifdef STDOUT_LOGGING
+  cta::log::StdoutLogger dl("dummy", "unitTest");
+#else
   cta::log::DummyLogger dl("dummy", "unitTest");
+#endif
   cta::log::LogContext lc(dl);
   // We need a dummy catalogue
   cta::catalogue::DummyCatalogue catalogue;
@@ -215,7 +227,11 @@ TEST(ObjectStore, GarbageCollectorArchiveQueue) {
 
 TEST(ObjectStore, GarbageCollectorDriveRegister) {
   // We will need a log object 
+#ifdef STDOUT_LOGGING
+  cta::log::StdoutLogger dl("dummy", "unitTest");
+#else
   cta::log::DummyLogger dl("dummy", "unitTest");
+#endif
   cta::log::LogContext lc(dl);
   // We need a dummy catalogue
   cta::catalogue::DummyCatalogue catalogue;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 60f6314790a5da554fd1e6b62edcb775c15f675b..d118913deb4dc0f726fd93e0b244c7b46225543a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -40,6 +40,7 @@ target_link_libraries(cta-unitTests
   ctaschedulerunittests
   ctadaemonunittests
   ctamediachangerunittests
+  ctamediachangeracsdaemonunittests
   ${GMOCK_LIB}
   gtest
   pthread)