diff --git a/castor/legacymsg/VdqmProxyTcpIp.cpp b/castor/legacymsg/VdqmProxyTcpIp.cpp
index 04e127b8f65239b7d37d32b1e8513eda90eed6f4..cd65089b9aab5ebee8d9e1319ec3e2f7f0931931 100644
--- a/castor/legacymsg/VdqmProxyTcpIp.cpp
+++ b/castor/legacymsg/VdqmProxyTcpIp.cpp
@@ -32,7 +32,9 @@
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-castor::legacymsg::VdqmProxyTcpIp::VdqmProxyTcpIp(log::Logger &log, const std::string &vdqmHostName, const unsigned short vdqmPort, const int netTimeout) throw():
+castor::legacymsg::VdqmProxyTcpIp::VdqmProxyTcpIp(log::Logger &log,
+  const std::string &vdqmHostName, const unsigned short vdqmPort,
+  const int netTimeout) throw():
   m_log(log),
   m_vdqmHostName(vdqmHostName),
   m_vdqmPort(vdqmPort),
@@ -48,7 +50,8 @@ castor::legacymsg::VdqmProxyTcpIp::~VdqmProxyTcpIp() throw() {
 //------------------------------------------------------------------------------
 // setDriveDown
 //------------------------------------------------------------------------------
-void castor::legacymsg::VdqmProxyTcpIp::setDriveDown(const std::string &server, const std::string &unitName, const std::string &dgn)  {
+void castor::legacymsg::VdqmProxyTcpIp::setDriveDown(const std::string &server,
+  const std::string &unitName, const std::string &dgn)  {
   try {
     legacymsg::VdqmDrvRqstMsgBody body;
     body.status = VDQM_UNIT_DOWN;
@@ -59,8 +62,9 @@ void castor::legacymsg::VdqmProxyTcpIp::setDriveDown(const std::string &server,
     setDriveStatus(body);
   } catch(castor::exception::Exception &ne) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to set status of tape drive " << unitName <<
-      " to down: " << ne.getMessage().str();
+    ex.getMessage() << "Failed to set state of tape drive"
+      ": server=" << server << " unitName=" << unitName << " dgn=" << dgn <<
+      " state=down: " << ne.getMessage().str();
     throw ex;
   }
 }
@@ -68,7 +72,8 @@ void castor::legacymsg::VdqmProxyTcpIp::setDriveDown(const std::string &server,
 //------------------------------------------------------------------------------
 // setDriveUp
 //------------------------------------------------------------------------------
-void castor::legacymsg::VdqmProxyTcpIp::setDriveUp(const std::string &server, const std::string &unitName, const std::string &dgn)  {
+void castor::legacymsg::VdqmProxyTcpIp::setDriveUp(const std::string &server,
+  const std::string &unitName, const std::string &dgn)  {
   try {
     legacymsg::VdqmDrvRqstMsgBody body;
     body.status = VDQM_UNIT_UP;
@@ -79,8 +84,9 @@ void castor::legacymsg::VdqmProxyTcpIp::setDriveUp(const std::string &server, co
     setDriveStatus(body);
   } catch(castor::exception::Exception &ne) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to set status of tape drive " << unitName <<
-      " to up: " << ne.getMessage().str();
+    ex.getMessage() << "Failed to set state of tape drive"
+      ": server=" << server << " unitName=" << unitName << " dgn=" << dgn <<
+      " state=up: " << ne.getMessage().str();
     throw ex;
   }
 }
@@ -88,7 +94,9 @@ void castor::legacymsg::VdqmProxyTcpIp::setDriveUp(const std::string &server, co
 //------------------------------------------------------------------------------
 // assignDrive
 //------------------------------------------------------------------------------
-void castor::legacymsg::VdqmProxyTcpIp::assignDrive(const std::string &server, const std::string &unitName, const std::string &dgn, const uint32_t mountTransactionId, const pid_t sessionPid)  {
+void castor::legacymsg::VdqmProxyTcpIp::assignDrive(const std::string &server,
+  const std::string &unitName, const std::string &dgn,
+  const uint32_t mountTransactionId, const pid_t sessionPid)  {
   try {
     legacymsg::VdqmDrvRqstMsgBody body;
     body.status = VDQM_UNIT_ASSIGN;
@@ -101,8 +109,10 @@ void castor::legacymsg::VdqmProxyTcpIp::assignDrive(const std::string &server, c
     setDriveStatus(body);
   } catch(castor::exception::Exception &ne) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to assign mount-session with process ID " <<
-      sessionPid << "to tape drive " << unitName << ": " << ne.getMessage().str();
+    ex.getMessage() << "Failed to assign drive"
+      ": server=" << server << " unitName=" << unitName << " dgn=" << dgn <<
+      " mountTransactionId=" << mountTransactionId << " sessionPid=" <<
+      sessionPid << ": " << ne.getMessage().str();
     throw ex;
   }
 }
@@ -126,8 +136,10 @@ void castor::legacymsg::VdqmProxyTcpIp::tapeMounted(const std::string &server,
     setDriveStatus(body);
   } catch(castor::exception::Exception &ne) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to notify vdqm that tape " << vid <<
-      " was mounted on tape drive " << unitName << ": " << ne.getMessage().str();
+    ex.getMessage() << "Failed to notify vdqm that tape is mounted"
+      ": server=" << server << " unitName=" << unitName << " dgn=" << dgn <<
+      " vid=" << vid << " sessionPid=" << sessionPid << ": " <<
+      ne.getMessage().str();
     throw ex;
   }
 }
@@ -155,8 +167,10 @@ void castor::legacymsg::VdqmProxyTcpIp::releaseDrive(const std::string &server,
     setDriveStatus(body);
   } catch(castor::exception::Exception &ne) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to release tape drive " << unitName << ": " <<
-      ne.getMessage().str();
+    ex.getMessage() << "Failed to release tape drive"
+      ": server=" << server << " unitName=" << unitName << " dgn=" << dgn <<
+      " forceUnmount=" << forceUnmount << " sessionPid=" << sessionPid << ": "
+      << ne.getMessage().str();
     throw ex;
   }
 }
@@ -164,7 +178,8 @@ void castor::legacymsg::VdqmProxyTcpIp::releaseDrive(const std::string &server,
 //------------------------------------------------------------------------------
 // setDriveStatus
 //------------------------------------------------------------------------------
-void castor::legacymsg::VdqmProxyTcpIp::setDriveStatus(const legacymsg::VdqmDrvRqstMsgBody &body)  {
+void castor::legacymsg::VdqmProxyTcpIp::setDriveStatus(
+  const legacymsg::VdqmDrvRqstMsgBody &body)  {
   castor::utils::SmartFd fd(connectToVdqm());
   writeDriveStatusMsg(fd.get(), body);
   readCommitAck(fd.get());
@@ -194,7 +209,8 @@ int castor::legacymsg::VdqmProxyTcpIp::connectToVdqm() const  {
 //-----------------------------------------------------------------------------
 // writeDriveStatusMsg
 //-----------------------------------------------------------------------------
-void castor::legacymsg::VdqmProxyTcpIp::writeDriveStatusMsg(const int fd, const legacymsg::VdqmDrvRqstMsgBody &body)  {
+void castor::legacymsg::VdqmProxyTcpIp::writeDriveStatusMsg(const int fd,
+  const legacymsg::VdqmDrvRqstMsgBody &body)  {
   char buf[VDQM_MSGBUFSIZ];
   const size_t len = legacymsg::marshal(buf, body);
 
@@ -252,7 +268,8 @@ void castor::legacymsg::VdqmProxyTcpIp::readCommitAck(const int fd)  {
 //-----------------------------------------------------------------------------
 // readAck
 //-----------------------------------------------------------------------------
-castor::legacymsg::MessageHeader castor::legacymsg::VdqmProxyTcpIp::readAck(const int fd)  {
+castor::legacymsg::MessageHeader castor::legacymsg::VdqmProxyTcpIp::readAck(
+  const int fd)  {
   char buf[12]; // Magic + type + len
   legacymsg::MessageHeader ack;
 
@@ -275,7 +292,8 @@ castor::legacymsg::MessageHeader castor::legacymsg::VdqmProxyTcpIp::readAck(cons
 //-----------------------------------------------------------------------------
 // readDriveStatusMsgHeader
 //-----------------------------------------------------------------------------
-castor::legacymsg::MessageHeader castor::legacymsg::VdqmProxyTcpIp::readDriveStatusMsgHeader(const int fd)  {
+castor::legacymsg::MessageHeader castor::legacymsg::VdqmProxyTcpIp::
+  readDriveStatusMsgHeader(const int fd) {
   char buf[12]; // Magic + type + len
   legacymsg::MessageHeader header;
 
@@ -316,7 +334,8 @@ castor::legacymsg::MessageHeader castor::legacymsg::VdqmProxyTcpIp::readDriveSta
 //-----------------------------------------------------------------------------
 // readDriveStatusMsgBody
 //-----------------------------------------------------------------------------
-castor::legacymsg::VdqmDrvRqstMsgBody castor::legacymsg::VdqmProxyTcpIp::readDriveStatusMsgBody(const int fd, const uint32_t bodyLen)  {
+castor::legacymsg::VdqmDrvRqstMsgBody castor::legacymsg::VdqmProxyTcpIp::
+  readDriveStatusMsgBody(const int fd, const uint32_t bodyLen)  {
   char buf[VDQM_MSGBUFSIZ];
 
   if(sizeof(buf) < bodyLen) {
@@ -368,7 +387,9 @@ void castor::legacymsg::VdqmProxyTcpIp::writeCommitAck(const int fd)  {
 //-----------------------------------------------------------------------------
 // tapeUnmounted
 //-----------------------------------------------------------------------------
-void  castor::legacymsg::VdqmProxyTcpIp::tapeUnmounted(const std::string &server, const std::string &unitName, const std::string &dgn, const std::string &vid)  {
+void  castor::legacymsg::VdqmProxyTcpIp::tapeUnmounted(
+  const std::string &server, const std::string &unitName,
+  const std::string &dgn, const std::string &vid)  {
   int status = VDQM_VOL_UNMOUNT;
 
   try {
diff --git a/castor/messages/CMakeLists.txt b/castor/messages/CMakeLists.txt
index 9db3e7871c2733554d2e54afb48c1493b6f9d94c..7e495ecdeea626e5c90981d8d84d49ca116365bf 100644
--- a/castor/messages/CMakeLists.txt
+++ b/castor/messages/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_library(castormessages SHARED
+  Exception.pb.cc
   ForkCleaner.pb.cc
   ForkDataTransfer.pb.cc
   ForkLabel.pb.cc
@@ -7,8 +8,9 @@ add_library(castormessages SHARED
   Heartbeat.pb.cc
   messages.cpp
   NotifyDrive.pb.cc
+  ProcessCrashed.pb.cc
+  ProcessExited.pb.cc
   ReplyContainer.cpp
-  Status.pb.cc
   StopProcessForker.pb.cc
   TapeserverProxy.cpp
   TapeserverProxyDummy.cpp
diff --git a/castor/messages/Status.pb.cc b/castor/messages/Exception.pb.cc
similarity index 69%
rename from castor/messages/Status.pb.cc
rename to castor/messages/Exception.pb.cc
index 639d2b444af00ae10691b099a7ea14c95ece6bf1..c22c5ae6073efe5da76f3757524f4e05bba55053 100644
--- a/castor/messages/Status.pb.cc
+++ b/castor/messages/Exception.pb.cc
@@ -1,7 +1,7 @@
 // Generated by the protocol buffer compiler.  DO NOT EDIT!
 
 #define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "Status.pb.h"
+#include "Exception.pb.h"
 #include <google/protobuf/stubs/once.h>
 #include <google/protobuf/io/coded_stream.h>
 #include <google/protobuf/wire_format_lite_inl.h>
@@ -15,35 +15,35 @@ namespace messages {
 
 namespace {
 
-const ::google::protobuf::Descriptor* Status_descriptor_ = NULL;
+const ::google::protobuf::Descriptor* Exception_descriptor_ = NULL;
 const ::google::protobuf::internal::GeneratedMessageReflection*
-  Status_reflection_ = NULL;
+  Exception_reflection_ = NULL;
 
 }  // namespace
 
 
-void protobuf_AssignDesc_Status_2eproto() {
-  protobuf_AddDesc_Status_2eproto();
+void protobuf_AssignDesc_Exception_2eproto() {
+  protobuf_AddDesc_Exception_2eproto();
   const ::google::protobuf::FileDescriptor* file =
     ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
-      "Status.proto");
+      "Exception.proto");
   GOOGLE_CHECK(file != NULL);
-  Status_descriptor_ = file->message_type(0);
-  static const int Status_offsets_[2] = {
-    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Status, status_),
-    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Status, message_),
+  Exception_descriptor_ = file->message_type(0);
+  static const int Exception_offsets_[2] = {
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Exception, code_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Exception, message_),
   };
-  Status_reflection_ =
+  Exception_reflection_ =
     new ::google::protobuf::internal::GeneratedMessageReflection(
-      Status_descriptor_,
-      Status::default_instance_,
-      Status_offsets_,
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Status, _has_bits_[0]),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Status, _unknown_fields_),
+      Exception_descriptor_,
+      Exception::default_instance_,
+      Exception_offsets_,
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Exception, _has_bits_[0]),
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Exception, _unknown_fields_),
       -1,
       ::google::protobuf::DescriptorPool::generated_pool(),
       ::google::protobuf::MessageFactory::generated_factory(),
-      sizeof(Status));
+      sizeof(Exception));
 }
 
 namespace {
@@ -51,80 +51,80 @@ namespace {
 GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
 inline void protobuf_AssignDescriptorsOnce() {
   ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
-                 &protobuf_AssignDesc_Status_2eproto);
+                 &protobuf_AssignDesc_Exception_2eproto);
 }
 
 void protobuf_RegisterTypes(const ::std::string&) {
   protobuf_AssignDescriptorsOnce();
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-    Status_descriptor_, &Status::default_instance());
+    Exception_descriptor_, &Exception::default_instance());
 }
 
 }  // namespace
 
-void protobuf_ShutdownFile_Status_2eproto() {
-  delete Status::default_instance_;
-  delete Status_reflection_;
+void protobuf_ShutdownFile_Exception_2eproto() {
+  delete Exception::default_instance_;
+  delete Exception_reflection_;
 }
 
-void protobuf_AddDesc_Status_2eproto() {
+void protobuf_AddDesc_Exception_2eproto() {
   static bool already_here = false;
   if (already_here) return;
   already_here = true;
   GOOGLE_PROTOBUF_VERIFY_VERSION;
 
   ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
-    "\n\014Status.proto\022\017castor.messages\")\n\006Statu"
-    "s\022\016\n\006status\030\001 \002(\r\022\017\n\007message\030\002 \002(\t", 74);
+    "\n\017Exception.proto\022\017castor.messages\"*\n\tEx"
+    "ception\022\014\n\004code\030\001 \002(\r\022\017\n\007message\030\002 \002(\t", 78);
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
-    "Status.proto", &protobuf_RegisterTypes);
-  Status::default_instance_ = new Status();
-  Status::default_instance_->InitAsDefaultInstance();
-  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_Status_2eproto);
+    "Exception.proto", &protobuf_RegisterTypes);
+  Exception::default_instance_ = new Exception();
+  Exception::default_instance_->InitAsDefaultInstance();
+  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_Exception_2eproto);
 }
 
 // Force AddDescriptors() to be called at static initialization time.
-struct StaticDescriptorInitializer_Status_2eproto {
-  StaticDescriptorInitializer_Status_2eproto() {
-    protobuf_AddDesc_Status_2eproto();
+struct StaticDescriptorInitializer_Exception_2eproto {
+  StaticDescriptorInitializer_Exception_2eproto() {
+    protobuf_AddDesc_Exception_2eproto();
   }
-} static_descriptor_initializer_Status_2eproto_;
+} static_descriptor_initializer_Exception_2eproto_;
 
 
 // ===================================================================
 
-const ::std::string Status::_default_message_;
+const ::std::string Exception::_default_message_;
 #ifndef _MSC_VER
-const int Status::kStatusFieldNumber;
-const int Status::kMessageFieldNumber;
+const int Exception::kCodeFieldNumber;
+const int Exception::kMessageFieldNumber;
 #endif  // !_MSC_VER
 
-Status::Status()
+Exception::Exception()
   : ::google::protobuf::Message() {
   SharedCtor();
 }
 
-void Status::InitAsDefaultInstance() {
+void Exception::InitAsDefaultInstance() {
 }
 
-Status::Status(const Status& from)
+Exception::Exception(const Exception& from)
   : ::google::protobuf::Message() {
   SharedCtor();
   MergeFrom(from);
 }
 
-void Status::SharedCtor() {
+void Exception::SharedCtor() {
   _cached_size_ = 0;
-  status_ = 0u;
+  code_ = 0u;
   message_ = const_cast< ::std::string*>(&_default_message_);
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
 }
 
-Status::~Status() {
+Exception::~Exception() {
   SharedDtor();
 }
 
-void Status::SharedDtor() {
+void Exception::SharedDtor() {
   if (message_ != &_default_message_) {
     delete message_;
   }
@@ -132,29 +132,29 @@ void Status::SharedDtor() {
   }
 }
 
-void Status::SetCachedSize(int size) const {
+void Exception::SetCachedSize(int size) const {
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
   _cached_size_ = size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
 }
-const ::google::protobuf::Descriptor* Status::descriptor() {
+const ::google::protobuf::Descriptor* Exception::descriptor() {
   protobuf_AssignDescriptorsOnce();
-  return Status_descriptor_;
+  return Exception_descriptor_;
 }
 
-const Status& Status::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_Status_2eproto();  return *default_instance_;
+const Exception& Exception::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_Exception_2eproto();  return *default_instance_;
 }
 
-Status* Status::default_instance_ = NULL;
+Exception* Exception::default_instance_ = NULL;
 
-Status* Status::New() const {
-  return new Status;
+Exception* Exception::New() const {
+  return new Exception;
 }
 
-void Status::Clear() {
+void Exception::Clear() {
   if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
-    status_ = 0u;
+    code_ = 0u;
     if (_has_bit(1)) {
       if (message_ != &_default_message_) {
         message_->clear();
@@ -165,19 +165,19 @@ void Status::Clear() {
   mutable_unknown_fields()->Clear();
 }
 
-bool Status::MergePartialFromCodedStream(
+bool Exception::MergePartialFromCodedStream(
     ::google::protobuf::io::CodedInputStream* input) {
 #define DO_(EXPRESSION) if (!(EXPRESSION)) return false
   ::google::protobuf::uint32 tag;
   while ((tag = input->ReadTag()) != 0) {
     switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
-      // required uint32 status = 1;
+      // required uint32 code = 1;
       case 1: {
         if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
             ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
-                 input, &status_)));
+                 input, &code_)));
           _set_bit(0);
         } else {
           goto handle_uninterpreted;
@@ -219,11 +219,11 @@ bool Status::MergePartialFromCodedStream(
 #undef DO_
 }
 
-void Status::SerializeWithCachedSizes(
+void Exception::SerializeWithCachedSizes(
     ::google::protobuf::io::CodedOutputStream* output) const {
-  // required uint32 status = 1;
+  // required uint32 code = 1;
   if (_has_bit(0)) {
-    ::google::protobuf::internal::WireFormatLite::WriteUInt32(1, this->status(), output);
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(1, this->code(), output);
   }
   
   // required string message = 2;
@@ -241,11 +241,11 @@ void Status::SerializeWithCachedSizes(
   }
 }
 
-::google::protobuf::uint8* Status::SerializeWithCachedSizesToArray(
+::google::protobuf::uint8* Exception::SerializeWithCachedSizesToArray(
     ::google::protobuf::uint8* target) const {
-  // required uint32 status = 1;
+  // required uint32 code = 1;
   if (_has_bit(0)) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(1, this->status(), target);
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(1, this->code(), target);
   }
   
   // required string message = 2;
@@ -265,15 +265,15 @@ void Status::SerializeWithCachedSizes(
   return target;
 }
 
-int Status::ByteSize() const {
+int Exception::ByteSize() const {
   int total_size = 0;
   
   if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
-    // required uint32 status = 1;
-    if (has_status()) {
+    // required uint32 code = 1;
+    if (has_code()) {
       total_size += 1 +
         ::google::protobuf::internal::WireFormatLite::UInt32Size(
-          this->status());
+          this->code());
     }
     
     // required string message = 2;
@@ -295,10 +295,10 @@ int Status::ByteSize() const {
   return total_size;
 }
 
-void Status::MergeFrom(const ::google::protobuf::Message& from) {
+void Exception::MergeFrom(const ::google::protobuf::Message& from) {
   GOOGLE_CHECK_NE(&from, this);
-  const Status* source =
-    ::google::protobuf::internal::dynamic_cast_if_available<const Status*>(
+  const Exception* source =
+    ::google::protobuf::internal::dynamic_cast_if_available<const Exception*>(
       &from);
   if (source == NULL) {
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
@@ -307,11 +307,11 @@ void Status::MergeFrom(const ::google::protobuf::Message& from) {
   }
 }
 
-void Status::MergeFrom(const Status& from) {
+void Exception::MergeFrom(const Exception& from) {
   GOOGLE_CHECK_NE(&from, this);
   if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
     if (from._has_bit(0)) {
-      set_status(from.status());
+      set_code(from.code());
     }
     if (from._has_bit(1)) {
       set_message(from.message());
@@ -320,27 +320,27 @@ void Status::MergeFrom(const Status& from) {
   mutable_unknown_fields()->MergeFrom(from.unknown_fields());
 }
 
-void Status::CopyFrom(const ::google::protobuf::Message& from) {
+void Exception::CopyFrom(const ::google::protobuf::Message& from) {
   if (&from == this) return;
   Clear();
   MergeFrom(from);
 }
 
-void Status::CopyFrom(const Status& from) {
+void Exception::CopyFrom(const Exception& from) {
   if (&from == this) return;
   Clear();
   MergeFrom(from);
 }
 
-bool Status::IsInitialized() const {
+bool Exception::IsInitialized() const {
   if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false;
   
   return true;
 }
 
-void Status::Swap(Status* other) {
+void Exception::Swap(Exception* other) {
   if (other != this) {
-    std::swap(status_, other->status_);
+    std::swap(code_, other->code_);
     std::swap(message_, other->message_);
     std::swap(_has_bits_[0], other->_has_bits_[0]);
     _unknown_fields_.Swap(&other->_unknown_fields_);
@@ -348,11 +348,11 @@ void Status::Swap(Status* other) {
   }
 }
 
-::google::protobuf::Metadata Status::GetMetadata() const {
+::google::protobuf::Metadata Exception::GetMetadata() const {
   protobuf_AssignDescriptorsOnce();
   ::google::protobuf::Metadata metadata;
-  metadata.descriptor = Status_descriptor_;
-  metadata.reflection = Status_reflection_;
+  metadata.descriptor = Exception_descriptor_;
+  metadata.reflection = Exception_reflection_;
   return metadata;
 }
 
diff --git a/castor/messages/Status.pb.h b/castor/messages/Exception.pb.h
similarity index 69%
rename from castor/messages/Status.pb.h
rename to castor/messages/Exception.pb.h
index 719d7c6e93ea1a6103e025fa0b4de1ab91634bc0..556407a84770bb625ebab1fb19a7f43ea9be8b66 100644
--- a/castor/messages/Status.pb.h
+++ b/castor/messages/Exception.pb.h
@@ -1,8 +1,8 @@
 // Generated by the protocol buffer compiler.  DO NOT EDIT!
-// source: Status.proto
+// source: Exception.proto
 
-#ifndef PROTOBUF_Status_2eproto__INCLUDED
-#define PROTOBUF_Status_2eproto__INCLUDED
+#ifndef PROTOBUF_Exception_2eproto__INCLUDED
+#define PROTOBUF_Exception_2eproto__INCLUDED
 
 #include <string>
 
@@ -29,22 +29,22 @@ namespace castor {
 namespace messages {
 
 // Internal implementation detail -- do not call these.
-void  protobuf_AddDesc_Status_2eproto();
-void protobuf_AssignDesc_Status_2eproto();
-void protobuf_ShutdownFile_Status_2eproto();
+void  protobuf_AddDesc_Exception_2eproto();
+void protobuf_AssignDesc_Exception_2eproto();
+void protobuf_ShutdownFile_Exception_2eproto();
 
-class Status;
+class Exception;
 
 // ===================================================================
 
-class Status : public ::google::protobuf::Message {
+class Exception : public ::google::protobuf::Message {
  public:
-  Status();
-  virtual ~Status();
+  Exception();
+  virtual ~Exception();
   
-  Status(const Status& from);
+  Exception(const Exception& from);
   
-  inline Status& operator=(const Status& from) {
+  inline Exception& operator=(const Exception& from) {
     CopyFrom(from);
     return *this;
   }
@@ -58,17 +58,17 @@ class Status : public ::google::protobuf::Message {
   }
   
   static const ::google::protobuf::Descriptor* descriptor();
-  static const Status& default_instance();
+  static const Exception& default_instance();
   
-  void Swap(Status* other);
+  void Swap(Exception* other);
   
   // implements Message ----------------------------------------------
   
-  Status* New() const;
+  Exception* New() const;
   void CopyFrom(const ::google::protobuf::Message& from);
   void MergeFrom(const ::google::protobuf::Message& from);
-  void CopyFrom(const Status& from);
-  void MergeFrom(const Status& from);
+  void CopyFrom(const Exception& from);
+  void MergeFrom(const Exception& from);
   void Clear();
   bool IsInitialized() const;
   
@@ -91,12 +91,12 @@ class Status : public ::google::protobuf::Message {
   
   // accessors -------------------------------------------------------
   
-  // required uint32 status = 1;
-  inline bool has_status() const;
-  inline void clear_status();
-  static const int kStatusFieldNumber = 1;
-  inline ::google::protobuf::uint32 status() const;
-  inline void set_status(::google::protobuf::uint32 value);
+  // required uint32 code = 1;
+  inline bool has_code() const;
+  inline void clear_code();
+  static const int kCodeFieldNumber = 1;
+  inline ::google::protobuf::uint32 code() const;
+  inline void set_code(::google::protobuf::uint32 value);
   
   // required string message = 2;
   inline bool has_message() const;
@@ -108,17 +108,17 @@ class Status : public ::google::protobuf::Message {
   inline void set_message(const char* value, size_t size);
   inline ::std::string* mutable_message();
   
-  // @@protoc_insertion_point(class_scope:castor.messages.Status)
+  // @@protoc_insertion_point(class_scope:castor.messages.Exception)
  private:
   ::google::protobuf::UnknownFieldSet _unknown_fields_;
   mutable int _cached_size_;
   
-  ::google::protobuf::uint32 status_;
+  ::google::protobuf::uint32 code_;
   ::std::string* message_;
   static const ::std::string _default_message_;
-  friend void  protobuf_AddDesc_Status_2eproto();
-  friend void protobuf_AssignDesc_Status_2eproto();
-  friend void protobuf_ShutdownFile_Status_2eproto();
+  friend void  protobuf_AddDesc_Exception_2eproto();
+  friend void protobuf_AssignDesc_Exception_2eproto();
+  friend void protobuf_ShutdownFile_Exception_2eproto();
   
   ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
   
@@ -134,66 +134,66 @@ class Status : public ::google::protobuf::Message {
   }
   
   void InitAsDefaultInstance();
-  static Status* default_instance_;
+  static Exception* default_instance_;
 };
 // ===================================================================
 
 
 // ===================================================================
 
-// Status
+// Exception
 
-// required uint32 status = 1;
-inline bool Status::has_status() const {
+// required uint32 code = 1;
+inline bool Exception::has_code() const {
   return _has_bit(0);
 }
-inline void Status::clear_status() {
-  status_ = 0u;
+inline void Exception::clear_code() {
+  code_ = 0u;
   _clear_bit(0);
 }
-inline ::google::protobuf::uint32 Status::status() const {
-  return status_;
+inline ::google::protobuf::uint32 Exception::code() const {
+  return code_;
 }
-inline void Status::set_status(::google::protobuf::uint32 value) {
+inline void Exception::set_code(::google::protobuf::uint32 value) {
   _set_bit(0);
-  status_ = value;
+  code_ = value;
 }
 
 // required string message = 2;
-inline bool Status::has_message() const {
+inline bool Exception::has_message() const {
   return _has_bit(1);
 }
-inline void Status::clear_message() {
+inline void Exception::clear_message() {
   if (message_ != &_default_message_) {
     message_->clear();
   }
   _clear_bit(1);
 }
-inline const ::std::string& Status::message() const {
+inline const ::std::string& Exception::message() const {
   return *message_;
 }
-inline void Status::set_message(const ::std::string& value) {
+inline void Exception::set_message(const ::std::string& value) {
   _set_bit(1);
   if (message_ == &_default_message_) {
     message_ = new ::std::string;
   }
   message_->assign(value);
 }
-inline void Status::set_message(const char* value) {
+inline void Exception::set_message(const char* value) {
   _set_bit(1);
   if (message_ == &_default_message_) {
     message_ = new ::std::string;
   }
   message_->assign(value);
 }
-inline void Status::set_message(const char* value, size_t size) {
+inline void Exception::set_message(const char* value, size_t size) {
   _set_bit(1);
   if (message_ == &_default_message_) {
     message_ = new ::std::string;
   }
   message_->assign(reinterpret_cast<const char*>(value), size);
 }
-inline ::std::string* Status::mutable_message() {
+inline ::std::string* Exception::mutable_message() {
   _set_bit(1);
   if (message_ == &_default_message_) {
     message_ = new ::std::string;
@@ -218,4 +218,4 @@ namespace protobuf {
 
 // @@protoc_insertion_point(global_scope)
 
-#endif  // PROTOBUF_Status_2eproto__INCLUDED
+#endif  // PROTOBUF_Exception_2eproto__INCLUDED
diff --git a/castor/messages/Status.proto b/castor/messages/Exception.proto
similarity index 71%
rename from castor/messages/Status.proto
rename to castor/messages/Exception.proto
index c414fa2e1816d4319ac96228dcf30c2414a867de..db47f7be674878dc42da1afe6c29b60a0907ef25 100644
--- a/castor/messages/Status.proto
+++ b/castor/messages/Exception.proto
@@ -18,13 +18,10 @@
 
 package castor.messages;
 
-message Status {
-  // Status value where 0 means success and non-zero means error.
-  required uint32 status = 1;
+message Exception {
+  // The error code
+  required uint32 code = 1;
 
-  // In the event of an error, in other words when the status field is set to a
-  // non-zero value, this field should contain a human-readable error-message.
-  // This field should explicitly be set to the empty string when the status
-  // field is 0.
+  // The error message
   required string message = 2;
 }
diff --git a/castor/messages/ForkDataTransfer.pb.cc b/castor/messages/ForkDataTransfer.pb.cc
index 506a2db0a6c030008c0b9fef572f213152544e13..b2af4c635f9fdc5833e9dc9284a52af1e769a050 100644
--- a/castor/messages/ForkDataTransfer.pb.cc
+++ b/castor/messages/ForkDataTransfer.pb.cc
@@ -29,8 +29,29 @@ void protobuf_AssignDesc_ForkDataTransfer_2eproto() {
       "ForkDataTransfer.proto");
   GOOGLE_CHECK(file != NULL);
   ForkDataTransfer_descriptor_ = file->message_type(0);
-  static const int ForkDataTransfer_offsets_[1] = {
+  static const int ForkDataTransfer_offsets_[22] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, unitname_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, dgn_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, devfilename_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, density_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, libraryslot_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, devtype_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, mounttransactionid_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, clientport_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, clienteuid_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, clientegid_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, clienthost_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, clientusername_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, memblocksize_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, nbmemblocks_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, badmirhandling_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, bulkrequestmigrationmaxbytes_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, bulkrequestmigrationmaxfiles_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, bulkrequestrecallmaxbytes_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, bulkrequestrecallmaxfiles_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, maxbytesbeforeflush_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, maxfilesbeforeflush_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ForkDataTransfer, diskthreadpoolsize_),
   };
   ForkDataTransfer_reflection_ =
     new ::google::protobuf::internal::GeneratedMessageReflection(
@@ -74,7 +95,20 @@ void protobuf_AddDesc_ForkDataTransfer_2eproto() {
 
   ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
     "\n\026ForkDataTransfer.proto\022\017castor.message"
-    "s\"$\n\020ForkDataTransfer\022\020\n\010unitname\030\001 \002(\t", 79);
+    "s\"\254\004\n\020ForkDataTransfer\022\020\n\010unitname\030\001 \002(\t"
+    "\022\013\n\003dgn\030\002 \002(\t\022\023\n\013devfilename\030\003 \002(\t\022\017\n\007de"
+    "nsity\030\004 \003(\t\022\023\n\013libraryslot\030\005 \002(\t\022\017\n\007devt"
+    "ype\030\006 \002(\t\022\032\n\022mounttransactionid\030\007 \002(\r\022\022\n"
+    "\nclientport\030\010 \002(\r\022\022\n\nclienteuid\030\t \002(\r\022\022\n"
+    "\nclientegid\030\n \002(\r\022\022\n\nclienthost\030\013 \002(\t\022\026\n"
+    "\016clientusername\030\014 \002(\t\022\024\n\014memblocksize\030\r "
+    "\002(\r\022\023\n\013nbmemblocks\030\016 \002(\r\022\026\n\016badmirhandli"
+    "ng\030\017 \002(\t\022$\n\034bulkrequestmigrationmaxbytes"
+    "\030\020 \002(\004\022$\n\034bulkrequestmigrationmaxfiles\030\021"
+    " \002(\004\022!\n\031bulkrequestrecallmaxbytes\030\022 \002(\004\022"
+    "!\n\031bulkrequestrecallmaxfiles\030\023 \002(\004\022\033\n\023ma"
+    "xbytesbeforeflush\030\024 \002(\004\022\033\n\023maxfilesbefor"
+    "eflush\030\025 \002(\004\022\032\n\022diskthreadpoolsize\030\026 \002(\r", 600);
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
     "ForkDataTransfer.proto", &protobuf_RegisterTypes);
   ForkDataTransfer::default_instance_ = new ForkDataTransfer();
@@ -93,8 +127,36 @@ struct StaticDescriptorInitializer_ForkDataTransfer_2eproto {
 // ===================================================================
 
 const ::std::string ForkDataTransfer::_default_unitname_;
+const ::std::string ForkDataTransfer::_default_dgn_;
+const ::std::string ForkDataTransfer::_default_devfilename_;
+const ::std::string ForkDataTransfer::_default_libraryslot_;
+const ::std::string ForkDataTransfer::_default_devtype_;
+const ::std::string ForkDataTransfer::_default_clienthost_;
+const ::std::string ForkDataTransfer::_default_clientusername_;
+const ::std::string ForkDataTransfer::_default_badmirhandling_;
 #ifndef _MSC_VER
 const int ForkDataTransfer::kUnitnameFieldNumber;
+const int ForkDataTransfer::kDgnFieldNumber;
+const int ForkDataTransfer::kDevfilenameFieldNumber;
+const int ForkDataTransfer::kDensityFieldNumber;
+const int ForkDataTransfer::kLibraryslotFieldNumber;
+const int ForkDataTransfer::kDevtypeFieldNumber;
+const int ForkDataTransfer::kMounttransactionidFieldNumber;
+const int ForkDataTransfer::kClientportFieldNumber;
+const int ForkDataTransfer::kClienteuidFieldNumber;
+const int ForkDataTransfer::kClientegidFieldNumber;
+const int ForkDataTransfer::kClienthostFieldNumber;
+const int ForkDataTransfer::kClientusernameFieldNumber;
+const int ForkDataTransfer::kMemblocksizeFieldNumber;
+const int ForkDataTransfer::kNbmemblocksFieldNumber;
+const int ForkDataTransfer::kBadmirhandlingFieldNumber;
+const int ForkDataTransfer::kBulkrequestmigrationmaxbytesFieldNumber;
+const int ForkDataTransfer::kBulkrequestmigrationmaxfilesFieldNumber;
+const int ForkDataTransfer::kBulkrequestrecallmaxbytesFieldNumber;
+const int ForkDataTransfer::kBulkrequestrecallmaxfilesFieldNumber;
+const int ForkDataTransfer::kMaxbytesbeforeflushFieldNumber;
+const int ForkDataTransfer::kMaxfilesbeforeflushFieldNumber;
+const int ForkDataTransfer::kDiskthreadpoolsizeFieldNumber;
 #endif  // !_MSC_VER
 
 ForkDataTransfer::ForkDataTransfer()
@@ -114,6 +176,26 @@ ForkDataTransfer::ForkDataTransfer(const ForkDataTransfer& from)
 void ForkDataTransfer::SharedCtor() {
   _cached_size_ = 0;
   unitname_ = const_cast< ::std::string*>(&_default_unitname_);
+  dgn_ = const_cast< ::std::string*>(&_default_dgn_);
+  devfilename_ = const_cast< ::std::string*>(&_default_devfilename_);
+  libraryslot_ = const_cast< ::std::string*>(&_default_libraryslot_);
+  devtype_ = const_cast< ::std::string*>(&_default_devtype_);
+  mounttransactionid_ = 0u;
+  clientport_ = 0u;
+  clienteuid_ = 0u;
+  clientegid_ = 0u;
+  clienthost_ = const_cast< ::std::string*>(&_default_clienthost_);
+  clientusername_ = const_cast< ::std::string*>(&_default_clientusername_);
+  memblocksize_ = 0u;
+  nbmemblocks_ = 0u;
+  badmirhandling_ = const_cast< ::std::string*>(&_default_badmirhandling_);
+  bulkrequestmigrationmaxbytes_ = GOOGLE_ULONGLONG(0);
+  bulkrequestmigrationmaxfiles_ = GOOGLE_ULONGLONG(0);
+  bulkrequestrecallmaxbytes_ = GOOGLE_ULONGLONG(0);
+  bulkrequestrecallmaxfiles_ = GOOGLE_ULONGLONG(0);
+  maxbytesbeforeflush_ = GOOGLE_ULONGLONG(0);
+  maxfilesbeforeflush_ = GOOGLE_ULONGLONG(0);
+  diskthreadpoolsize_ = 0u;
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
 }
 
@@ -125,6 +207,27 @@ void ForkDataTransfer::SharedDtor() {
   if (unitname_ != &_default_unitname_) {
     delete unitname_;
   }
+  if (dgn_ != &_default_dgn_) {
+    delete dgn_;
+  }
+  if (devfilename_ != &_default_devfilename_) {
+    delete devfilename_;
+  }
+  if (libraryslot_ != &_default_libraryslot_) {
+    delete libraryslot_;
+  }
+  if (devtype_ != &_default_devtype_) {
+    delete devtype_;
+  }
+  if (clienthost_ != &_default_clienthost_) {
+    delete clienthost_;
+  }
+  if (clientusername_ != &_default_clientusername_) {
+    delete clientusername_;
+  }
+  if (badmirhandling_ != &_default_badmirhandling_) {
+    delete badmirhandling_;
+  }
   if (this != default_instance_) {
   }
 }
@@ -156,7 +259,60 @@ void ForkDataTransfer::Clear() {
         unitname_->clear();
       }
     }
+    if (_has_bit(1)) {
+      if (dgn_ != &_default_dgn_) {
+        dgn_->clear();
+      }
+    }
+    if (_has_bit(2)) {
+      if (devfilename_ != &_default_devfilename_) {
+        devfilename_->clear();
+      }
+    }
+    if (_has_bit(4)) {
+      if (libraryslot_ != &_default_libraryslot_) {
+        libraryslot_->clear();
+      }
+    }
+    if (_has_bit(5)) {
+      if (devtype_ != &_default_devtype_) {
+        devtype_->clear();
+      }
+    }
+    mounttransactionid_ = 0u;
+    clientport_ = 0u;
   }
+  if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+    clienteuid_ = 0u;
+    clientegid_ = 0u;
+    if (_has_bit(10)) {
+      if (clienthost_ != &_default_clienthost_) {
+        clienthost_->clear();
+      }
+    }
+    if (_has_bit(11)) {
+      if (clientusername_ != &_default_clientusername_) {
+        clientusername_->clear();
+      }
+    }
+    memblocksize_ = 0u;
+    nbmemblocks_ = 0u;
+    if (_has_bit(14)) {
+      if (badmirhandling_ != &_default_badmirhandling_) {
+        badmirhandling_->clear();
+      }
+    }
+    bulkrequestmigrationmaxbytes_ = GOOGLE_ULONGLONG(0);
+  }
+  if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) {
+    bulkrequestmigrationmaxfiles_ = GOOGLE_ULONGLONG(0);
+    bulkrequestrecallmaxbytes_ = GOOGLE_ULONGLONG(0);
+    bulkrequestrecallmaxfiles_ = GOOGLE_ULONGLONG(0);
+    maxbytesbeforeflush_ = GOOGLE_ULONGLONG(0);
+    maxfilesbeforeflush_ = GOOGLE_ULONGLONG(0);
+    diskthreadpoolsize_ = 0u;
+  }
+  density_.Clear();
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
   mutable_unknown_fields()->Clear();
 }
@@ -179,6 +335,351 @@ bool ForkDataTransfer::MergePartialFromCodedStream(
         } else {
           goto handle_uninterpreted;
         }
+        if (input->ExpectTag(18)) goto parse_dgn;
+        break;
+      }
+      
+      // required string dgn = 2;
+      case 2: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_dgn:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_dgn()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->dgn().data(), this->dgn().length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(26)) goto parse_devfilename;
+        break;
+      }
+      
+      // required string devfilename = 3;
+      case 3: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_devfilename:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_devfilename()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->devfilename().data(), this->devfilename().length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(34)) goto parse_density;
+        break;
+      }
+      
+      // repeated string density = 4;
+      case 4: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_density:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->add_density()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->density(0).data(), this->density(0).length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(34)) goto parse_density;
+        if (input->ExpectTag(42)) goto parse_libraryslot;
+        break;
+      }
+      
+      // required string libraryslot = 5;
+      case 5: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_libraryslot:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_libraryslot()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->libraryslot().data(), this->libraryslot().length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(50)) goto parse_devtype;
+        break;
+      }
+      
+      // required string devtype = 6;
+      case 6: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_devtype:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_devtype()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->devtype().data(), this->devtype().length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(56)) goto parse_mounttransactionid;
+        break;
+      }
+      
+      // required uint32 mounttransactionid = 7;
+      case 7: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_mounttransactionid:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &mounttransactionid_)));
+          _set_bit(6);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(64)) goto parse_clientport;
+        break;
+      }
+      
+      // required uint32 clientport = 8;
+      case 8: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_clientport:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &clientport_)));
+          _set_bit(7);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(72)) goto parse_clienteuid;
+        break;
+      }
+      
+      // required uint32 clienteuid = 9;
+      case 9: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_clienteuid:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &clienteuid_)));
+          _set_bit(8);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(80)) goto parse_clientegid;
+        break;
+      }
+      
+      // required uint32 clientegid = 10;
+      case 10: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_clientegid:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &clientegid_)));
+          _set_bit(9);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(90)) goto parse_clienthost;
+        break;
+      }
+      
+      // required string clienthost = 11;
+      case 11: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_clienthost:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_clienthost()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->clienthost().data(), this->clienthost().length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(98)) goto parse_clientusername;
+        break;
+      }
+      
+      // required string clientusername = 12;
+      case 12: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_clientusername:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_clientusername()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->clientusername().data(), this->clientusername().length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(104)) goto parse_memblocksize;
+        break;
+      }
+      
+      // required uint32 memblocksize = 13;
+      case 13: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_memblocksize:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &memblocksize_)));
+          _set_bit(12);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(112)) goto parse_nbmemblocks;
+        break;
+      }
+      
+      // required uint32 nbmemblocks = 14;
+      case 14: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_nbmemblocks:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &nbmemblocks_)));
+          _set_bit(13);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(122)) goto parse_badmirhandling;
+        break;
+      }
+      
+      // required string badmirhandling = 15;
+      case 15: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_badmirhandling:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_badmirhandling()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+            this->badmirhandling().data(), this->badmirhandling().length(),
+            ::google::protobuf::internal::WireFormat::PARSE);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(128)) goto parse_bulkrequestmigrationmaxbytes;
+        break;
+      }
+      
+      // required uint64 bulkrequestmigrationmaxbytes = 16;
+      case 16: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_bulkrequestmigrationmaxbytes:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &bulkrequestmigrationmaxbytes_)));
+          _set_bit(15);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(136)) goto parse_bulkrequestmigrationmaxfiles;
+        break;
+      }
+      
+      // required uint64 bulkrequestmigrationmaxfiles = 17;
+      case 17: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_bulkrequestmigrationmaxfiles:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &bulkrequestmigrationmaxfiles_)));
+          _set_bit(16);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(144)) goto parse_bulkrequestrecallmaxbytes;
+        break;
+      }
+      
+      // required uint64 bulkrequestrecallmaxbytes = 18;
+      case 18: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_bulkrequestrecallmaxbytes:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &bulkrequestrecallmaxbytes_)));
+          _set_bit(17);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(152)) goto parse_bulkrequestrecallmaxfiles;
+        break;
+      }
+      
+      // required uint64 bulkrequestrecallmaxfiles = 19;
+      case 19: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_bulkrequestrecallmaxfiles:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &bulkrequestrecallmaxfiles_)));
+          _set_bit(18);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(160)) goto parse_maxbytesbeforeflush;
+        break;
+      }
+      
+      // required uint64 maxbytesbeforeflush = 20;
+      case 20: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_maxbytesbeforeflush:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &maxbytesbeforeflush_)));
+          _set_bit(19);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(168)) goto parse_maxfilesbeforeflush;
+        break;
+      }
+      
+      // required uint64 maxfilesbeforeflush = 21;
+      case 21: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_maxfilesbeforeflush:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &maxfilesbeforeflush_)));
+          _set_bit(20);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(176)) goto parse_diskthreadpoolsize;
+        break;
+      }
+      
+      // required uint32 diskthreadpoolsize = 22;
+      case 22: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_diskthreadpoolsize:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &diskthreadpoolsize_)));
+          _set_bit(21);
+        } else {
+          goto handle_uninterpreted;
+        }
         if (input->ExpectAtEnd()) return true;
         break;
       }
@@ -210,6 +711,143 @@ void ForkDataTransfer::SerializeWithCachedSizes(
       1, this->unitname(), output);
   }
   
+  // required string dgn = 2;
+  if (_has_bit(1)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->dgn().data(), this->dgn().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      2, this->dgn(), output);
+  }
+  
+  // required string devfilename = 3;
+  if (_has_bit(2)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->devfilename().data(), this->devfilename().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      3, this->devfilename(), output);
+  }
+  
+  // repeated string density = 4;
+  for (int i = 0; i < this->density_size(); i++) {
+  ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+    this->density(i).data(), this->density(i).length(),
+    ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      4, this->density(i), output);
+  }
+  
+  // required string libraryslot = 5;
+  if (_has_bit(4)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->libraryslot().data(), this->libraryslot().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      5, this->libraryslot(), output);
+  }
+  
+  // required string devtype = 6;
+  if (_has_bit(5)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->devtype().data(), this->devtype().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      6, this->devtype(), output);
+  }
+  
+  // required uint32 mounttransactionid = 7;
+  if (_has_bit(6)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(7, this->mounttransactionid(), output);
+  }
+  
+  // required uint32 clientport = 8;
+  if (_has_bit(7)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(8, this->clientport(), output);
+  }
+  
+  // required uint32 clienteuid = 9;
+  if (_has_bit(8)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(9, this->clienteuid(), output);
+  }
+  
+  // required uint32 clientegid = 10;
+  if (_has_bit(9)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(10, this->clientegid(), output);
+  }
+  
+  // required string clienthost = 11;
+  if (_has_bit(10)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->clienthost().data(), this->clienthost().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      11, this->clienthost(), output);
+  }
+  
+  // required string clientusername = 12;
+  if (_has_bit(11)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->clientusername().data(), this->clientusername().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      12, this->clientusername(), output);
+  }
+  
+  // required uint32 memblocksize = 13;
+  if (_has_bit(12)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(13, this->memblocksize(), output);
+  }
+  
+  // required uint32 nbmemblocks = 14;
+  if (_has_bit(13)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(14, this->nbmemblocks(), output);
+  }
+  
+  // required string badmirhandling = 15;
+  if (_has_bit(14)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->badmirhandling().data(), this->badmirhandling().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    ::google::protobuf::internal::WireFormatLite::WriteString(
+      15, this->badmirhandling(), output);
+  }
+  
+  // required uint64 bulkrequestmigrationmaxbytes = 16;
+  if (_has_bit(15)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(16, this->bulkrequestmigrationmaxbytes(), output);
+  }
+  
+  // required uint64 bulkrequestmigrationmaxfiles = 17;
+  if (_has_bit(16)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(17, this->bulkrequestmigrationmaxfiles(), output);
+  }
+  
+  // required uint64 bulkrequestrecallmaxbytes = 18;
+  if (_has_bit(17)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(18, this->bulkrequestrecallmaxbytes(), output);
+  }
+  
+  // required uint64 bulkrequestrecallmaxfiles = 19;
+  if (_has_bit(18)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(19, this->bulkrequestrecallmaxfiles(), output);
+  }
+  
+  // required uint64 maxbytesbeforeflush = 20;
+  if (_has_bit(19)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(20, this->maxbytesbeforeflush(), output);
+  }
+  
+  // required uint64 maxfilesbeforeflush = 21;
+  if (_has_bit(20)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(21, this->maxfilesbeforeflush(), output);
+  }
+  
+  // required uint32 diskthreadpoolsize = 22;
+  if (_has_bit(21)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(22, this->diskthreadpoolsize(), output);
+  }
+  
   if (!unknown_fields().empty()) {
     ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
         unknown_fields(), output);
@@ -228,6 +866,150 @@ void ForkDataTransfer::SerializeWithCachedSizes(
         1, this->unitname(), target);
   }
   
+  // required string dgn = 2;
+  if (_has_bit(1)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->dgn().data(), this->dgn().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        2, this->dgn(), target);
+  }
+  
+  // required string devfilename = 3;
+  if (_has_bit(2)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->devfilename().data(), this->devfilename().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        3, this->devfilename(), target);
+  }
+  
+  // repeated string density = 4;
+  for (int i = 0; i < this->density_size(); i++) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->density(i).data(), this->density(i).length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target = ::google::protobuf::internal::WireFormatLite::
+      WriteStringToArray(4, this->density(i), target);
+  }
+  
+  // required string libraryslot = 5;
+  if (_has_bit(4)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->libraryslot().data(), this->libraryslot().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        5, this->libraryslot(), target);
+  }
+  
+  // required string devtype = 6;
+  if (_has_bit(5)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->devtype().data(), this->devtype().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        6, this->devtype(), target);
+  }
+  
+  // required uint32 mounttransactionid = 7;
+  if (_has_bit(6)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(7, this->mounttransactionid(), target);
+  }
+  
+  // required uint32 clientport = 8;
+  if (_has_bit(7)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(8, this->clientport(), target);
+  }
+  
+  // required uint32 clienteuid = 9;
+  if (_has_bit(8)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(9, this->clienteuid(), target);
+  }
+  
+  // required uint32 clientegid = 10;
+  if (_has_bit(9)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(10, this->clientegid(), target);
+  }
+  
+  // required string clienthost = 11;
+  if (_has_bit(10)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->clienthost().data(), this->clienthost().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        11, this->clienthost(), target);
+  }
+  
+  // required string clientusername = 12;
+  if (_has_bit(11)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->clientusername().data(), this->clientusername().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        12, this->clientusername(), target);
+  }
+  
+  // required uint32 memblocksize = 13;
+  if (_has_bit(12)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(13, this->memblocksize(), target);
+  }
+  
+  // required uint32 nbmemblocks = 14;
+  if (_has_bit(13)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(14, this->nbmemblocks(), target);
+  }
+  
+  // required string badmirhandling = 15;
+  if (_has_bit(14)) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+      this->badmirhandling().data(), this->badmirhandling().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE);
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        15, this->badmirhandling(), target);
+  }
+  
+  // required uint64 bulkrequestmigrationmaxbytes = 16;
+  if (_has_bit(15)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(16, this->bulkrequestmigrationmaxbytes(), target);
+  }
+  
+  // required uint64 bulkrequestmigrationmaxfiles = 17;
+  if (_has_bit(16)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(17, this->bulkrequestmigrationmaxfiles(), target);
+  }
+  
+  // required uint64 bulkrequestrecallmaxbytes = 18;
+  if (_has_bit(17)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(18, this->bulkrequestrecallmaxbytes(), target);
+  }
+  
+  // required uint64 bulkrequestrecallmaxfiles = 19;
+  if (_has_bit(18)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(19, this->bulkrequestrecallmaxfiles(), target);
+  }
+  
+  // required uint64 maxbytesbeforeflush = 20;
+  if (_has_bit(19)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(20, this->maxbytesbeforeflush(), target);
+  }
+  
+  // required uint64 maxfilesbeforeflush = 21;
+  if (_has_bit(20)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(21, this->maxfilesbeforeflush(), target);
+  }
+  
+  // required uint32 diskthreadpoolsize = 22;
+  if (_has_bit(21)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(22, this->diskthreadpoolsize(), target);
+  }
+  
   if (!unknown_fields().empty()) {
     target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
         unknown_fields(), target);
@@ -246,7 +1028,158 @@ int ForkDataTransfer::ByteSize() const {
           this->unitname());
     }
     
+    // required string dgn = 2;
+    if (has_dgn()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->dgn());
+    }
+    
+    // required string devfilename = 3;
+    if (has_devfilename()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->devfilename());
+    }
+    
+    // required string libraryslot = 5;
+    if (has_libraryslot()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->libraryslot());
+    }
+    
+    // required string devtype = 6;
+    if (has_devtype()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->devtype());
+    }
+    
+    // required uint32 mounttransactionid = 7;
+    if (has_mounttransactionid()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->mounttransactionid());
+    }
+    
+    // required uint32 clientport = 8;
+    if (has_clientport()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->clientport());
+    }
+    
+  }
+  if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+    // required uint32 clienteuid = 9;
+    if (has_clienteuid()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->clienteuid());
+    }
+    
+    // required uint32 clientegid = 10;
+    if (has_clientegid()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->clientegid());
+    }
+    
+    // required string clienthost = 11;
+    if (has_clienthost()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->clienthost());
+    }
+    
+    // required string clientusername = 12;
+    if (has_clientusername()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->clientusername());
+    }
+    
+    // required uint32 memblocksize = 13;
+    if (has_memblocksize()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->memblocksize());
+    }
+    
+    // required uint32 nbmemblocks = 14;
+    if (has_nbmemblocks()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->nbmemblocks());
+    }
+    
+    // required string badmirhandling = 15;
+    if (has_badmirhandling()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->badmirhandling());
+    }
+    
+    // required uint64 bulkrequestmigrationmaxbytes = 16;
+    if (has_bulkrequestmigrationmaxbytes()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->bulkrequestmigrationmaxbytes());
+    }
+    
   }
+  if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) {
+    // required uint64 bulkrequestmigrationmaxfiles = 17;
+    if (has_bulkrequestmigrationmaxfiles()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->bulkrequestmigrationmaxfiles());
+    }
+    
+    // required uint64 bulkrequestrecallmaxbytes = 18;
+    if (has_bulkrequestrecallmaxbytes()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->bulkrequestrecallmaxbytes());
+    }
+    
+    // required uint64 bulkrequestrecallmaxfiles = 19;
+    if (has_bulkrequestrecallmaxfiles()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->bulkrequestrecallmaxfiles());
+    }
+    
+    // required uint64 maxbytesbeforeflush = 20;
+    if (has_maxbytesbeforeflush()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->maxbytesbeforeflush());
+    }
+    
+    // required uint64 maxfilesbeforeflush = 21;
+    if (has_maxfilesbeforeflush()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->maxfilesbeforeflush());
+    }
+    
+    // required uint32 diskthreadpoolsize = 22;
+    if (has_diskthreadpoolsize()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->diskthreadpoolsize());
+    }
+    
+  }
+  // repeated string density = 4;
+  total_size += 1 * this->density_size();
+  for (int i = 0; i < this->density_size(); i++) {
+    total_size += ::google::protobuf::internal::WireFormatLite::StringSize(
+      this->density(i));
+  }
+  
   if (!unknown_fields().empty()) {
     total_size +=
       ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
@@ -272,10 +1205,75 @@ void ForkDataTransfer::MergeFrom(const ::google::protobuf::Message& from) {
 
 void ForkDataTransfer::MergeFrom(const ForkDataTransfer& from) {
   GOOGLE_CHECK_NE(&from, this);
+  density_.MergeFrom(from.density_);
   if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
     if (from._has_bit(0)) {
       set_unitname(from.unitname());
     }
+    if (from._has_bit(1)) {
+      set_dgn(from.dgn());
+    }
+    if (from._has_bit(2)) {
+      set_devfilename(from.devfilename());
+    }
+    if (from._has_bit(4)) {
+      set_libraryslot(from.libraryslot());
+    }
+    if (from._has_bit(5)) {
+      set_devtype(from.devtype());
+    }
+    if (from._has_bit(6)) {
+      set_mounttransactionid(from.mounttransactionid());
+    }
+    if (from._has_bit(7)) {
+      set_clientport(from.clientport());
+    }
+  }
+  if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+    if (from._has_bit(8)) {
+      set_clienteuid(from.clienteuid());
+    }
+    if (from._has_bit(9)) {
+      set_clientegid(from.clientegid());
+    }
+    if (from._has_bit(10)) {
+      set_clienthost(from.clienthost());
+    }
+    if (from._has_bit(11)) {
+      set_clientusername(from.clientusername());
+    }
+    if (from._has_bit(12)) {
+      set_memblocksize(from.memblocksize());
+    }
+    if (from._has_bit(13)) {
+      set_nbmemblocks(from.nbmemblocks());
+    }
+    if (from._has_bit(14)) {
+      set_badmirhandling(from.badmirhandling());
+    }
+    if (from._has_bit(15)) {
+      set_bulkrequestmigrationmaxbytes(from.bulkrequestmigrationmaxbytes());
+    }
+  }
+  if (from._has_bits_[16 / 32] & (0xffu << (16 % 32))) {
+    if (from._has_bit(16)) {
+      set_bulkrequestmigrationmaxfiles(from.bulkrequestmigrationmaxfiles());
+    }
+    if (from._has_bit(17)) {
+      set_bulkrequestrecallmaxbytes(from.bulkrequestrecallmaxbytes());
+    }
+    if (from._has_bit(18)) {
+      set_bulkrequestrecallmaxfiles(from.bulkrequestrecallmaxfiles());
+    }
+    if (from._has_bit(19)) {
+      set_maxbytesbeforeflush(from.maxbytesbeforeflush());
+    }
+    if (from._has_bit(20)) {
+      set_maxfilesbeforeflush(from.maxfilesbeforeflush());
+    }
+    if (from._has_bit(21)) {
+      set_diskthreadpoolsize(from.diskthreadpoolsize());
+    }
   }
   mutable_unknown_fields()->MergeFrom(from.unknown_fields());
 }
@@ -293,7 +1291,7 @@ void ForkDataTransfer::CopyFrom(const ForkDataTransfer& from) {
 }
 
 bool ForkDataTransfer::IsInitialized() const {
-  if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+  if ((_has_bits_[0] & 0x003ffff7) != 0x003ffff7) return false;
   
   return true;
 }
@@ -301,6 +1299,27 @@ bool ForkDataTransfer::IsInitialized() const {
 void ForkDataTransfer::Swap(ForkDataTransfer* other) {
   if (other != this) {
     std::swap(unitname_, other->unitname_);
+    std::swap(dgn_, other->dgn_);
+    std::swap(devfilename_, other->devfilename_);
+    density_.Swap(&other->density_);
+    std::swap(libraryslot_, other->libraryslot_);
+    std::swap(devtype_, other->devtype_);
+    std::swap(mounttransactionid_, other->mounttransactionid_);
+    std::swap(clientport_, other->clientport_);
+    std::swap(clienteuid_, other->clienteuid_);
+    std::swap(clientegid_, other->clientegid_);
+    std::swap(clienthost_, other->clienthost_);
+    std::swap(clientusername_, other->clientusername_);
+    std::swap(memblocksize_, other->memblocksize_);
+    std::swap(nbmemblocks_, other->nbmemblocks_);
+    std::swap(badmirhandling_, other->badmirhandling_);
+    std::swap(bulkrequestmigrationmaxbytes_, other->bulkrequestmigrationmaxbytes_);
+    std::swap(bulkrequestmigrationmaxfiles_, other->bulkrequestmigrationmaxfiles_);
+    std::swap(bulkrequestrecallmaxbytes_, other->bulkrequestrecallmaxbytes_);
+    std::swap(bulkrequestrecallmaxfiles_, other->bulkrequestrecallmaxfiles_);
+    std::swap(maxbytesbeforeflush_, other->maxbytesbeforeflush_);
+    std::swap(maxfilesbeforeflush_, other->maxfilesbeforeflush_);
+    std::swap(diskthreadpoolsize_, other->diskthreadpoolsize_);
     std::swap(_has_bits_[0], other->_has_bits_[0]);
     _unknown_fields_.Swap(&other->_unknown_fields_);
     std::swap(_cached_size_, other->_cached_size_);
diff --git a/castor/messages/ForkDataTransfer.pb.h b/castor/messages/ForkDataTransfer.pb.h
index e5db556d3815d65c3abfdf33d1f1bce3f8818033..32c20ceece135439af6d809fff3a3f51c578b68f 100644
--- a/castor/messages/ForkDataTransfer.pb.h
+++ b/castor/messages/ForkDataTransfer.pb.h
@@ -101,6 +101,183 @@ class ForkDataTransfer : public ::google::protobuf::Message {
   inline void set_unitname(const char* value, size_t size);
   inline ::std::string* mutable_unitname();
   
+  // required string dgn = 2;
+  inline bool has_dgn() const;
+  inline void clear_dgn();
+  static const int kDgnFieldNumber = 2;
+  inline const ::std::string& dgn() const;
+  inline void set_dgn(const ::std::string& value);
+  inline void set_dgn(const char* value);
+  inline void set_dgn(const char* value, size_t size);
+  inline ::std::string* mutable_dgn();
+  
+  // required string devfilename = 3;
+  inline bool has_devfilename() const;
+  inline void clear_devfilename();
+  static const int kDevfilenameFieldNumber = 3;
+  inline const ::std::string& devfilename() const;
+  inline void set_devfilename(const ::std::string& value);
+  inline void set_devfilename(const char* value);
+  inline void set_devfilename(const char* value, size_t size);
+  inline ::std::string* mutable_devfilename();
+  
+  // repeated string density = 4;
+  inline int density_size() const;
+  inline void clear_density();
+  static const int kDensityFieldNumber = 4;
+  inline const ::std::string& density(int index) const;
+  inline ::std::string* mutable_density(int index);
+  inline void set_density(int index, const ::std::string& value);
+  inline void set_density(int index, const char* value);
+  inline void set_density(int index, const char* value, size_t size);
+  inline ::std::string* add_density();
+  inline void add_density(const ::std::string& value);
+  inline void add_density(const char* value);
+  inline void add_density(const char* value, size_t size);
+  inline const ::google::protobuf::RepeatedPtrField< ::std::string>& density() const;
+  inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_density();
+  
+  // required string libraryslot = 5;
+  inline bool has_libraryslot() const;
+  inline void clear_libraryslot();
+  static const int kLibraryslotFieldNumber = 5;
+  inline const ::std::string& libraryslot() const;
+  inline void set_libraryslot(const ::std::string& value);
+  inline void set_libraryslot(const char* value);
+  inline void set_libraryslot(const char* value, size_t size);
+  inline ::std::string* mutable_libraryslot();
+  
+  // required string devtype = 6;
+  inline bool has_devtype() const;
+  inline void clear_devtype();
+  static const int kDevtypeFieldNumber = 6;
+  inline const ::std::string& devtype() const;
+  inline void set_devtype(const ::std::string& value);
+  inline void set_devtype(const char* value);
+  inline void set_devtype(const char* value, size_t size);
+  inline ::std::string* mutable_devtype();
+  
+  // required uint32 mounttransactionid = 7;
+  inline bool has_mounttransactionid() const;
+  inline void clear_mounttransactionid();
+  static const int kMounttransactionidFieldNumber = 7;
+  inline ::google::protobuf::uint32 mounttransactionid() const;
+  inline void set_mounttransactionid(::google::protobuf::uint32 value);
+  
+  // required uint32 clientport = 8;
+  inline bool has_clientport() const;
+  inline void clear_clientport();
+  static const int kClientportFieldNumber = 8;
+  inline ::google::protobuf::uint32 clientport() const;
+  inline void set_clientport(::google::protobuf::uint32 value);
+  
+  // required uint32 clienteuid = 9;
+  inline bool has_clienteuid() const;
+  inline void clear_clienteuid();
+  static const int kClienteuidFieldNumber = 9;
+  inline ::google::protobuf::uint32 clienteuid() const;
+  inline void set_clienteuid(::google::protobuf::uint32 value);
+  
+  // required uint32 clientegid = 10;
+  inline bool has_clientegid() const;
+  inline void clear_clientegid();
+  static const int kClientegidFieldNumber = 10;
+  inline ::google::protobuf::uint32 clientegid() const;
+  inline void set_clientegid(::google::protobuf::uint32 value);
+  
+  // required string clienthost = 11;
+  inline bool has_clienthost() const;
+  inline void clear_clienthost();
+  static const int kClienthostFieldNumber = 11;
+  inline const ::std::string& clienthost() const;
+  inline void set_clienthost(const ::std::string& value);
+  inline void set_clienthost(const char* value);
+  inline void set_clienthost(const char* value, size_t size);
+  inline ::std::string* mutable_clienthost();
+  
+  // required string clientusername = 12;
+  inline bool has_clientusername() const;
+  inline void clear_clientusername();
+  static const int kClientusernameFieldNumber = 12;
+  inline const ::std::string& clientusername() const;
+  inline void set_clientusername(const ::std::string& value);
+  inline void set_clientusername(const char* value);
+  inline void set_clientusername(const char* value, size_t size);
+  inline ::std::string* mutable_clientusername();
+  
+  // required uint32 memblocksize = 13;
+  inline bool has_memblocksize() const;
+  inline void clear_memblocksize();
+  static const int kMemblocksizeFieldNumber = 13;
+  inline ::google::protobuf::uint32 memblocksize() const;
+  inline void set_memblocksize(::google::protobuf::uint32 value);
+  
+  // required uint32 nbmemblocks = 14;
+  inline bool has_nbmemblocks() const;
+  inline void clear_nbmemblocks();
+  static const int kNbmemblocksFieldNumber = 14;
+  inline ::google::protobuf::uint32 nbmemblocks() const;
+  inline void set_nbmemblocks(::google::protobuf::uint32 value);
+  
+  // required string badmirhandling = 15;
+  inline bool has_badmirhandling() const;
+  inline void clear_badmirhandling();
+  static const int kBadmirhandlingFieldNumber = 15;
+  inline const ::std::string& badmirhandling() const;
+  inline void set_badmirhandling(const ::std::string& value);
+  inline void set_badmirhandling(const char* value);
+  inline void set_badmirhandling(const char* value, size_t size);
+  inline ::std::string* mutable_badmirhandling();
+  
+  // required uint64 bulkrequestmigrationmaxbytes = 16;
+  inline bool has_bulkrequestmigrationmaxbytes() const;
+  inline void clear_bulkrequestmigrationmaxbytes();
+  static const int kBulkrequestmigrationmaxbytesFieldNumber = 16;
+  inline ::google::protobuf::uint64 bulkrequestmigrationmaxbytes() const;
+  inline void set_bulkrequestmigrationmaxbytes(::google::protobuf::uint64 value);
+  
+  // required uint64 bulkrequestmigrationmaxfiles = 17;
+  inline bool has_bulkrequestmigrationmaxfiles() const;
+  inline void clear_bulkrequestmigrationmaxfiles();
+  static const int kBulkrequestmigrationmaxfilesFieldNumber = 17;
+  inline ::google::protobuf::uint64 bulkrequestmigrationmaxfiles() const;
+  inline void set_bulkrequestmigrationmaxfiles(::google::protobuf::uint64 value);
+  
+  // required uint64 bulkrequestrecallmaxbytes = 18;
+  inline bool has_bulkrequestrecallmaxbytes() const;
+  inline void clear_bulkrequestrecallmaxbytes();
+  static const int kBulkrequestrecallmaxbytesFieldNumber = 18;
+  inline ::google::protobuf::uint64 bulkrequestrecallmaxbytes() const;
+  inline void set_bulkrequestrecallmaxbytes(::google::protobuf::uint64 value);
+  
+  // required uint64 bulkrequestrecallmaxfiles = 19;
+  inline bool has_bulkrequestrecallmaxfiles() const;
+  inline void clear_bulkrequestrecallmaxfiles();
+  static const int kBulkrequestrecallmaxfilesFieldNumber = 19;
+  inline ::google::protobuf::uint64 bulkrequestrecallmaxfiles() const;
+  inline void set_bulkrequestrecallmaxfiles(::google::protobuf::uint64 value);
+  
+  // required uint64 maxbytesbeforeflush = 20;
+  inline bool has_maxbytesbeforeflush() const;
+  inline void clear_maxbytesbeforeflush();
+  static const int kMaxbytesbeforeflushFieldNumber = 20;
+  inline ::google::protobuf::uint64 maxbytesbeforeflush() const;
+  inline void set_maxbytesbeforeflush(::google::protobuf::uint64 value);
+  
+  // required uint64 maxfilesbeforeflush = 21;
+  inline bool has_maxfilesbeforeflush() const;
+  inline void clear_maxfilesbeforeflush();
+  static const int kMaxfilesbeforeflushFieldNumber = 21;
+  inline ::google::protobuf::uint64 maxfilesbeforeflush() const;
+  inline void set_maxfilesbeforeflush(::google::protobuf::uint64 value);
+  
+  // required uint32 diskthreadpoolsize = 22;
+  inline bool has_diskthreadpoolsize() const;
+  inline void clear_diskthreadpoolsize();
+  static const int kDiskthreadpoolsizeFieldNumber = 22;
+  inline ::google::protobuf::uint32 diskthreadpoolsize() const;
+  inline void set_diskthreadpoolsize(::google::protobuf::uint32 value);
+  
   // @@protoc_insertion_point(class_scope:castor.messages.ForkDataTransfer)
  private:
   ::google::protobuf::UnknownFieldSet _unknown_fields_;
@@ -108,11 +285,39 @@ class ForkDataTransfer : public ::google::protobuf::Message {
   
   ::std::string* unitname_;
   static const ::std::string _default_unitname_;
+  ::std::string* dgn_;
+  static const ::std::string _default_dgn_;
+  ::std::string* devfilename_;
+  static const ::std::string _default_devfilename_;
+  ::google::protobuf::RepeatedPtrField< ::std::string> density_;
+  ::std::string* libraryslot_;
+  static const ::std::string _default_libraryslot_;
+  ::std::string* devtype_;
+  static const ::std::string _default_devtype_;
+  ::google::protobuf::uint32 mounttransactionid_;
+  ::google::protobuf::uint32 clientport_;
+  ::google::protobuf::uint32 clienteuid_;
+  ::google::protobuf::uint32 clientegid_;
+  ::std::string* clienthost_;
+  static const ::std::string _default_clienthost_;
+  ::std::string* clientusername_;
+  static const ::std::string _default_clientusername_;
+  ::google::protobuf::uint32 memblocksize_;
+  ::google::protobuf::uint32 nbmemblocks_;
+  ::std::string* badmirhandling_;
+  static const ::std::string _default_badmirhandling_;
+  ::google::protobuf::uint64 bulkrequestmigrationmaxbytes_;
+  ::google::protobuf::uint64 bulkrequestmigrationmaxfiles_;
+  ::google::protobuf::uint64 bulkrequestrecallmaxbytes_;
+  ::google::protobuf::uint64 bulkrequestrecallmaxfiles_;
+  ::google::protobuf::uint64 maxbytesbeforeflush_;
+  ::google::protobuf::uint64 maxfilesbeforeflush_;
+  ::google::protobuf::uint32 diskthreadpoolsize_;
   friend void  protobuf_AddDesc_ForkDataTransfer_2eproto();
   friend void protobuf_AssignDesc_ForkDataTransfer_2eproto();
   friend void protobuf_ShutdownFile_ForkDataTransfer_2eproto();
   
-  ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
+  ::google::protobuf::uint32 _has_bits_[(22 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
   inline bool _has_bit(int index) const {
@@ -177,6 +382,552 @@ inline ::std::string* ForkDataTransfer::mutable_unitname() {
   return unitname_;
 }
 
+// required string dgn = 2;
+inline bool ForkDataTransfer::has_dgn() const {
+  return _has_bit(1);
+}
+inline void ForkDataTransfer::clear_dgn() {
+  if (dgn_ != &_default_dgn_) {
+    dgn_->clear();
+  }
+  _clear_bit(1);
+}
+inline const ::std::string& ForkDataTransfer::dgn() const {
+  return *dgn_;
+}
+inline void ForkDataTransfer::set_dgn(const ::std::string& value) {
+  _set_bit(1);
+  if (dgn_ == &_default_dgn_) {
+    dgn_ = new ::std::string;
+  }
+  dgn_->assign(value);
+}
+inline void ForkDataTransfer::set_dgn(const char* value) {
+  _set_bit(1);
+  if (dgn_ == &_default_dgn_) {
+    dgn_ = new ::std::string;
+  }
+  dgn_->assign(value);
+}
+inline void ForkDataTransfer::set_dgn(const char* value, size_t size) {
+  _set_bit(1);
+  if (dgn_ == &_default_dgn_) {
+    dgn_ = new ::std::string;
+  }
+  dgn_->assign(reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::mutable_dgn() {
+  _set_bit(1);
+  if (dgn_ == &_default_dgn_) {
+    dgn_ = new ::std::string;
+  }
+  return dgn_;
+}
+
+// required string devfilename = 3;
+inline bool ForkDataTransfer::has_devfilename() const {
+  return _has_bit(2);
+}
+inline void ForkDataTransfer::clear_devfilename() {
+  if (devfilename_ != &_default_devfilename_) {
+    devfilename_->clear();
+  }
+  _clear_bit(2);
+}
+inline const ::std::string& ForkDataTransfer::devfilename() const {
+  return *devfilename_;
+}
+inline void ForkDataTransfer::set_devfilename(const ::std::string& value) {
+  _set_bit(2);
+  if (devfilename_ == &_default_devfilename_) {
+    devfilename_ = new ::std::string;
+  }
+  devfilename_->assign(value);
+}
+inline void ForkDataTransfer::set_devfilename(const char* value) {
+  _set_bit(2);
+  if (devfilename_ == &_default_devfilename_) {
+    devfilename_ = new ::std::string;
+  }
+  devfilename_->assign(value);
+}
+inline void ForkDataTransfer::set_devfilename(const char* value, size_t size) {
+  _set_bit(2);
+  if (devfilename_ == &_default_devfilename_) {
+    devfilename_ = new ::std::string;
+  }
+  devfilename_->assign(reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::mutable_devfilename() {
+  _set_bit(2);
+  if (devfilename_ == &_default_devfilename_) {
+    devfilename_ = new ::std::string;
+  }
+  return devfilename_;
+}
+
+// repeated string density = 4;
+inline int ForkDataTransfer::density_size() const {
+  return density_.size();
+}
+inline void ForkDataTransfer::clear_density() {
+  density_.Clear();
+}
+inline const ::std::string& ForkDataTransfer::density(int index) const {
+  return density_.Get(index);
+}
+inline ::std::string* ForkDataTransfer::mutable_density(int index) {
+  return density_.Mutable(index);
+}
+inline void ForkDataTransfer::set_density(int index, const ::std::string& value) {
+  density_.Mutable(index)->assign(value);
+}
+inline void ForkDataTransfer::set_density(int index, const char* value) {
+  density_.Mutable(index)->assign(value);
+}
+inline void ForkDataTransfer::set_density(int index, const char* value, size_t size) {
+  density_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::add_density() {
+  return density_.Add();
+}
+inline void ForkDataTransfer::add_density(const ::std::string& value) {
+  density_.Add()->assign(value);
+}
+inline void ForkDataTransfer::add_density(const char* value) {
+  density_.Add()->assign(value);
+}
+inline void ForkDataTransfer::add_density(const char* value, size_t size) {
+  density_.Add()->assign(reinterpret_cast<const char*>(value), size);
+}
+inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
+ForkDataTransfer::density() const {
+  return density_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::std::string>*
+ForkDataTransfer::mutable_density() {
+  return &density_;
+}
+
+// required string libraryslot = 5;
+inline bool ForkDataTransfer::has_libraryslot() const {
+  return _has_bit(4);
+}
+inline void ForkDataTransfer::clear_libraryslot() {
+  if (libraryslot_ != &_default_libraryslot_) {
+    libraryslot_->clear();
+  }
+  _clear_bit(4);
+}
+inline const ::std::string& ForkDataTransfer::libraryslot() const {
+  return *libraryslot_;
+}
+inline void ForkDataTransfer::set_libraryslot(const ::std::string& value) {
+  _set_bit(4);
+  if (libraryslot_ == &_default_libraryslot_) {
+    libraryslot_ = new ::std::string;
+  }
+  libraryslot_->assign(value);
+}
+inline void ForkDataTransfer::set_libraryslot(const char* value) {
+  _set_bit(4);
+  if (libraryslot_ == &_default_libraryslot_) {
+    libraryslot_ = new ::std::string;
+  }
+  libraryslot_->assign(value);
+}
+inline void ForkDataTransfer::set_libraryslot(const char* value, size_t size) {
+  _set_bit(4);
+  if (libraryslot_ == &_default_libraryslot_) {
+    libraryslot_ = new ::std::string;
+  }
+  libraryslot_->assign(reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::mutable_libraryslot() {
+  _set_bit(4);
+  if (libraryslot_ == &_default_libraryslot_) {
+    libraryslot_ = new ::std::string;
+  }
+  return libraryslot_;
+}
+
+// required string devtype = 6;
+inline bool ForkDataTransfer::has_devtype() const {
+  return _has_bit(5);
+}
+inline void ForkDataTransfer::clear_devtype() {
+  if (devtype_ != &_default_devtype_) {
+    devtype_->clear();
+  }
+  _clear_bit(5);
+}
+inline const ::std::string& ForkDataTransfer::devtype() const {
+  return *devtype_;
+}
+inline void ForkDataTransfer::set_devtype(const ::std::string& value) {
+  _set_bit(5);
+  if (devtype_ == &_default_devtype_) {
+    devtype_ = new ::std::string;
+  }
+  devtype_->assign(value);
+}
+inline void ForkDataTransfer::set_devtype(const char* value) {
+  _set_bit(5);
+  if (devtype_ == &_default_devtype_) {
+    devtype_ = new ::std::string;
+  }
+  devtype_->assign(value);
+}
+inline void ForkDataTransfer::set_devtype(const char* value, size_t size) {
+  _set_bit(5);
+  if (devtype_ == &_default_devtype_) {
+    devtype_ = new ::std::string;
+  }
+  devtype_->assign(reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::mutable_devtype() {
+  _set_bit(5);
+  if (devtype_ == &_default_devtype_) {
+    devtype_ = new ::std::string;
+  }
+  return devtype_;
+}
+
+// required uint32 mounttransactionid = 7;
+inline bool ForkDataTransfer::has_mounttransactionid() const {
+  return _has_bit(6);
+}
+inline void ForkDataTransfer::clear_mounttransactionid() {
+  mounttransactionid_ = 0u;
+  _clear_bit(6);
+}
+inline ::google::protobuf::uint32 ForkDataTransfer::mounttransactionid() const {
+  return mounttransactionid_;
+}
+inline void ForkDataTransfer::set_mounttransactionid(::google::protobuf::uint32 value) {
+  _set_bit(6);
+  mounttransactionid_ = value;
+}
+
+// required uint32 clientport = 8;
+inline bool ForkDataTransfer::has_clientport() const {
+  return _has_bit(7);
+}
+inline void ForkDataTransfer::clear_clientport() {
+  clientport_ = 0u;
+  _clear_bit(7);
+}
+inline ::google::protobuf::uint32 ForkDataTransfer::clientport() const {
+  return clientport_;
+}
+inline void ForkDataTransfer::set_clientport(::google::protobuf::uint32 value) {
+  _set_bit(7);
+  clientport_ = value;
+}
+
+// required uint32 clienteuid = 9;
+inline bool ForkDataTransfer::has_clienteuid() const {
+  return _has_bit(8);
+}
+inline void ForkDataTransfer::clear_clienteuid() {
+  clienteuid_ = 0u;
+  _clear_bit(8);
+}
+inline ::google::protobuf::uint32 ForkDataTransfer::clienteuid() const {
+  return clienteuid_;
+}
+inline void ForkDataTransfer::set_clienteuid(::google::protobuf::uint32 value) {
+  _set_bit(8);
+  clienteuid_ = value;
+}
+
+// required uint32 clientegid = 10;
+inline bool ForkDataTransfer::has_clientegid() const {
+  return _has_bit(9);
+}
+inline void ForkDataTransfer::clear_clientegid() {
+  clientegid_ = 0u;
+  _clear_bit(9);
+}
+inline ::google::protobuf::uint32 ForkDataTransfer::clientegid() const {
+  return clientegid_;
+}
+inline void ForkDataTransfer::set_clientegid(::google::protobuf::uint32 value) {
+  _set_bit(9);
+  clientegid_ = value;
+}
+
+// required string clienthost = 11;
+inline bool ForkDataTransfer::has_clienthost() const {
+  return _has_bit(10);
+}
+inline void ForkDataTransfer::clear_clienthost() {
+  if (clienthost_ != &_default_clienthost_) {
+    clienthost_->clear();
+  }
+  _clear_bit(10);
+}
+inline const ::std::string& ForkDataTransfer::clienthost() const {
+  return *clienthost_;
+}
+inline void ForkDataTransfer::set_clienthost(const ::std::string& value) {
+  _set_bit(10);
+  if (clienthost_ == &_default_clienthost_) {
+    clienthost_ = new ::std::string;
+  }
+  clienthost_->assign(value);
+}
+inline void ForkDataTransfer::set_clienthost(const char* value) {
+  _set_bit(10);
+  if (clienthost_ == &_default_clienthost_) {
+    clienthost_ = new ::std::string;
+  }
+  clienthost_->assign(value);
+}
+inline void ForkDataTransfer::set_clienthost(const char* value, size_t size) {
+  _set_bit(10);
+  if (clienthost_ == &_default_clienthost_) {
+    clienthost_ = new ::std::string;
+  }
+  clienthost_->assign(reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::mutable_clienthost() {
+  _set_bit(10);
+  if (clienthost_ == &_default_clienthost_) {
+    clienthost_ = new ::std::string;
+  }
+  return clienthost_;
+}
+
+// required string clientusername = 12;
+inline bool ForkDataTransfer::has_clientusername() const {
+  return _has_bit(11);
+}
+inline void ForkDataTransfer::clear_clientusername() {
+  if (clientusername_ != &_default_clientusername_) {
+    clientusername_->clear();
+  }
+  _clear_bit(11);
+}
+inline const ::std::string& ForkDataTransfer::clientusername() const {
+  return *clientusername_;
+}
+inline void ForkDataTransfer::set_clientusername(const ::std::string& value) {
+  _set_bit(11);
+  if (clientusername_ == &_default_clientusername_) {
+    clientusername_ = new ::std::string;
+  }
+  clientusername_->assign(value);
+}
+inline void ForkDataTransfer::set_clientusername(const char* value) {
+  _set_bit(11);
+  if (clientusername_ == &_default_clientusername_) {
+    clientusername_ = new ::std::string;
+  }
+  clientusername_->assign(value);
+}
+inline void ForkDataTransfer::set_clientusername(const char* value, size_t size) {
+  _set_bit(11);
+  if (clientusername_ == &_default_clientusername_) {
+    clientusername_ = new ::std::string;
+  }
+  clientusername_->assign(reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::mutable_clientusername() {
+  _set_bit(11);
+  if (clientusername_ == &_default_clientusername_) {
+    clientusername_ = new ::std::string;
+  }
+  return clientusername_;
+}
+
+// required uint32 memblocksize = 13;
+inline bool ForkDataTransfer::has_memblocksize() const {
+  return _has_bit(12);
+}
+inline void ForkDataTransfer::clear_memblocksize() {
+  memblocksize_ = 0u;
+  _clear_bit(12);
+}
+inline ::google::protobuf::uint32 ForkDataTransfer::memblocksize() const {
+  return memblocksize_;
+}
+inline void ForkDataTransfer::set_memblocksize(::google::protobuf::uint32 value) {
+  _set_bit(12);
+  memblocksize_ = value;
+}
+
+// required uint32 nbmemblocks = 14;
+inline bool ForkDataTransfer::has_nbmemblocks() const {
+  return _has_bit(13);
+}
+inline void ForkDataTransfer::clear_nbmemblocks() {
+  nbmemblocks_ = 0u;
+  _clear_bit(13);
+}
+inline ::google::protobuf::uint32 ForkDataTransfer::nbmemblocks() const {
+  return nbmemblocks_;
+}
+inline void ForkDataTransfer::set_nbmemblocks(::google::protobuf::uint32 value) {
+  _set_bit(13);
+  nbmemblocks_ = value;
+}
+
+// required string badmirhandling = 15;
+inline bool ForkDataTransfer::has_badmirhandling() const {
+  return _has_bit(14);
+}
+inline void ForkDataTransfer::clear_badmirhandling() {
+  if (badmirhandling_ != &_default_badmirhandling_) {
+    badmirhandling_->clear();
+  }
+  _clear_bit(14);
+}
+inline const ::std::string& ForkDataTransfer::badmirhandling() const {
+  return *badmirhandling_;
+}
+inline void ForkDataTransfer::set_badmirhandling(const ::std::string& value) {
+  _set_bit(14);
+  if (badmirhandling_ == &_default_badmirhandling_) {
+    badmirhandling_ = new ::std::string;
+  }
+  badmirhandling_->assign(value);
+}
+inline void ForkDataTransfer::set_badmirhandling(const char* value) {
+  _set_bit(14);
+  if (badmirhandling_ == &_default_badmirhandling_) {
+    badmirhandling_ = new ::std::string;
+  }
+  badmirhandling_->assign(value);
+}
+inline void ForkDataTransfer::set_badmirhandling(const char* value, size_t size) {
+  _set_bit(14);
+  if (badmirhandling_ == &_default_badmirhandling_) {
+    badmirhandling_ = new ::std::string;
+  }
+  badmirhandling_->assign(reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* ForkDataTransfer::mutable_badmirhandling() {
+  _set_bit(14);
+  if (badmirhandling_ == &_default_badmirhandling_) {
+    badmirhandling_ = new ::std::string;
+  }
+  return badmirhandling_;
+}
+
+// required uint64 bulkrequestmigrationmaxbytes = 16;
+inline bool ForkDataTransfer::has_bulkrequestmigrationmaxbytes() const {
+  return _has_bit(15);
+}
+inline void ForkDataTransfer::clear_bulkrequestmigrationmaxbytes() {
+  bulkrequestmigrationmaxbytes_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(15);
+}
+inline ::google::protobuf::uint64 ForkDataTransfer::bulkrequestmigrationmaxbytes() const {
+  return bulkrequestmigrationmaxbytes_;
+}
+inline void ForkDataTransfer::set_bulkrequestmigrationmaxbytes(::google::protobuf::uint64 value) {
+  _set_bit(15);
+  bulkrequestmigrationmaxbytes_ = value;
+}
+
+// required uint64 bulkrequestmigrationmaxfiles = 17;
+inline bool ForkDataTransfer::has_bulkrequestmigrationmaxfiles() const {
+  return _has_bit(16);
+}
+inline void ForkDataTransfer::clear_bulkrequestmigrationmaxfiles() {
+  bulkrequestmigrationmaxfiles_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(16);
+}
+inline ::google::protobuf::uint64 ForkDataTransfer::bulkrequestmigrationmaxfiles() const {
+  return bulkrequestmigrationmaxfiles_;
+}
+inline void ForkDataTransfer::set_bulkrequestmigrationmaxfiles(::google::protobuf::uint64 value) {
+  _set_bit(16);
+  bulkrequestmigrationmaxfiles_ = value;
+}
+
+// required uint64 bulkrequestrecallmaxbytes = 18;
+inline bool ForkDataTransfer::has_bulkrequestrecallmaxbytes() const {
+  return _has_bit(17);
+}
+inline void ForkDataTransfer::clear_bulkrequestrecallmaxbytes() {
+  bulkrequestrecallmaxbytes_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(17);
+}
+inline ::google::protobuf::uint64 ForkDataTransfer::bulkrequestrecallmaxbytes() const {
+  return bulkrequestrecallmaxbytes_;
+}
+inline void ForkDataTransfer::set_bulkrequestrecallmaxbytes(::google::protobuf::uint64 value) {
+  _set_bit(17);
+  bulkrequestrecallmaxbytes_ = value;
+}
+
+// required uint64 bulkrequestrecallmaxfiles = 19;
+inline bool ForkDataTransfer::has_bulkrequestrecallmaxfiles() const {
+  return _has_bit(18);
+}
+inline void ForkDataTransfer::clear_bulkrequestrecallmaxfiles() {
+  bulkrequestrecallmaxfiles_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(18);
+}
+inline ::google::protobuf::uint64 ForkDataTransfer::bulkrequestrecallmaxfiles() const {
+  return bulkrequestrecallmaxfiles_;
+}
+inline void ForkDataTransfer::set_bulkrequestrecallmaxfiles(::google::protobuf::uint64 value) {
+  _set_bit(18);
+  bulkrequestrecallmaxfiles_ = value;
+}
+
+// required uint64 maxbytesbeforeflush = 20;
+inline bool ForkDataTransfer::has_maxbytesbeforeflush() const {
+  return _has_bit(19);
+}
+inline void ForkDataTransfer::clear_maxbytesbeforeflush() {
+  maxbytesbeforeflush_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(19);
+}
+inline ::google::protobuf::uint64 ForkDataTransfer::maxbytesbeforeflush() const {
+  return maxbytesbeforeflush_;
+}
+inline void ForkDataTransfer::set_maxbytesbeforeflush(::google::protobuf::uint64 value) {
+  _set_bit(19);
+  maxbytesbeforeflush_ = value;
+}
+
+// required uint64 maxfilesbeforeflush = 21;
+inline bool ForkDataTransfer::has_maxfilesbeforeflush() const {
+  return _has_bit(20);
+}
+inline void ForkDataTransfer::clear_maxfilesbeforeflush() {
+  maxfilesbeforeflush_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(20);
+}
+inline ::google::protobuf::uint64 ForkDataTransfer::maxfilesbeforeflush() const {
+  return maxfilesbeforeflush_;
+}
+inline void ForkDataTransfer::set_maxfilesbeforeflush(::google::protobuf::uint64 value) {
+  _set_bit(20);
+  maxfilesbeforeflush_ = value;
+}
+
+// required uint32 diskthreadpoolsize = 22;
+inline bool ForkDataTransfer::has_diskthreadpoolsize() const {
+  return _has_bit(21);
+}
+inline void ForkDataTransfer::clear_diskthreadpoolsize() {
+  diskthreadpoolsize_ = 0u;
+  _clear_bit(21);
+}
+inline ::google::protobuf::uint32 ForkDataTransfer::diskthreadpoolsize() const {
+  return diskthreadpoolsize_;
+}
+inline void ForkDataTransfer::set_diskthreadpoolsize(::google::protobuf::uint32 value) {
+  _set_bit(21);
+  diskthreadpoolsize_ = value;
+}
+
 
 // @@protoc_insertion_point(namespace_scope)
 
diff --git a/castor/messages/ForkDataTransfer.proto b/castor/messages/ForkDataTransfer.proto
index 9f68f3a6c016578003d370b4ee286e4ac654508f..22aa1f76fe1716c471813cc7a32f476409530574 100644
--- a/castor/messages/ForkDataTransfer.proto
+++ b/castor/messages/ForkDataTransfer.proto
@@ -21,5 +21,31 @@
 package castor.messages;
 
 message ForkDataTransfer {
+  // Description of the tape drive
   required string unitname = 1;
+  required string dgn = 2;
+  required string devfilename = 3;
+  repeated string density = 4;
+  required string libraryslot = 5;
+  required string devtype = 6;
+
+  // The job from the VDQM
+  required uint32 mounttransactionid = 7;
+  required uint32 clientport = 8;
+  required uint32 clienteuid = 9;
+  required uint32 clientegid = 10;
+  required string clienthost = 11;
+  required string clientusername = 12;
+
+  // Configuration parameters of the session
+  required uint32 memblocksize = 13;
+  required uint32 nbmemblocks = 14;
+  required string badmirhandling = 15;
+  required uint64 bulkrequestmigrationmaxbytes = 16;
+  required uint64 bulkrequestmigrationmaxfiles = 17;
+  required uint64 bulkrequestrecallmaxbytes = 18;
+  required uint64 bulkrequestrecallmaxfiles = 19;
+  required uint64 maxbytesbeforeflush = 20;
+  required uint64 maxfilesbeforeflush = 21;
+  required uint32 diskthreadpoolsize = 22;
 }
diff --git a/castor/messages/ProcessCrashed.pb.cc b/castor/messages/ProcessCrashed.pb.cc
new file mode 100644
index 0000000000000000000000000000000000000000..660455da037698a08026a621307f322d45abd1c1
--- /dev/null
+++ b/castor/messages/ProcessCrashed.pb.cc
@@ -0,0 +1,348 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+
+#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+#include "ProcessCrashed.pb.h"
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format_lite_inl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+
+namespace castor {
+namespace messages {
+
+namespace {
+
+const ::google::protobuf::Descriptor* ProcessCrashed_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+  ProcessCrashed_reflection_ = NULL;
+
+}  // namespace
+
+
+void protobuf_AssignDesc_ProcessCrashed_2eproto() {
+  protobuf_AddDesc_ProcessCrashed_2eproto();
+  const ::google::protobuf::FileDescriptor* file =
+    ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
+      "ProcessCrashed.proto");
+  GOOGLE_CHECK(file != NULL);
+  ProcessCrashed_descriptor_ = file->message_type(0);
+  static const int ProcessCrashed_offsets_[2] = {
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessCrashed, pid_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessCrashed, signal_),
+  };
+  ProcessCrashed_reflection_ =
+    new ::google::protobuf::internal::GeneratedMessageReflection(
+      ProcessCrashed_descriptor_,
+      ProcessCrashed::default_instance_,
+      ProcessCrashed_offsets_,
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessCrashed, _has_bits_[0]),
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessCrashed, _unknown_fields_),
+      -1,
+      ::google::protobuf::DescriptorPool::generated_pool(),
+      ::google::protobuf::MessageFactory::generated_factory(),
+      sizeof(ProcessCrashed));
+}
+
+namespace {
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
+inline void protobuf_AssignDescriptorsOnce() {
+  ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
+                 &protobuf_AssignDesc_ProcessCrashed_2eproto);
+}
+
+void protobuf_RegisterTypes(const ::std::string&) {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+    ProcessCrashed_descriptor_, &ProcessCrashed::default_instance());
+}
+
+}  // namespace
+
+void protobuf_ShutdownFile_ProcessCrashed_2eproto() {
+  delete ProcessCrashed::default_instance_;
+  delete ProcessCrashed_reflection_;
+}
+
+void protobuf_AddDesc_ProcessCrashed_2eproto() {
+  static bool already_here = false;
+  if (already_here) return;
+  already_here = true;
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
+    "\n\024ProcessCrashed.proto\022\017castor.messages\""
+    "-\n\016ProcessCrashed\022\013\n\003pid\030\001 \002(\004\022\016\n\006signal"
+    "\030\002 \002(\r", 86);
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
+    "ProcessCrashed.proto", &protobuf_RegisterTypes);
+  ProcessCrashed::default_instance_ = new ProcessCrashed();
+  ProcessCrashed::default_instance_->InitAsDefaultInstance();
+  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_ProcessCrashed_2eproto);
+}
+
+// Force AddDescriptors() to be called at static initialization time.
+struct StaticDescriptorInitializer_ProcessCrashed_2eproto {
+  StaticDescriptorInitializer_ProcessCrashed_2eproto() {
+    protobuf_AddDesc_ProcessCrashed_2eproto();
+  }
+} static_descriptor_initializer_ProcessCrashed_2eproto_;
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int ProcessCrashed::kPidFieldNumber;
+const int ProcessCrashed::kSignalFieldNumber;
+#endif  // !_MSC_VER
+
+ProcessCrashed::ProcessCrashed()
+  : ::google::protobuf::Message() {
+  SharedCtor();
+}
+
+void ProcessCrashed::InitAsDefaultInstance() {
+}
+
+ProcessCrashed::ProcessCrashed(const ProcessCrashed& from)
+  : ::google::protobuf::Message() {
+  SharedCtor();
+  MergeFrom(from);
+}
+
+void ProcessCrashed::SharedCtor() {
+  _cached_size_ = 0;
+  pid_ = GOOGLE_ULONGLONG(0);
+  signal_ = 0u;
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+ProcessCrashed::~ProcessCrashed() {
+  SharedDtor();
+}
+
+void ProcessCrashed::SharedDtor() {
+  if (this != default_instance_) {
+  }
+}
+
+void ProcessCrashed::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* ProcessCrashed::descriptor() {
+  protobuf_AssignDescriptorsOnce();
+  return ProcessCrashed_descriptor_;
+}
+
+const ProcessCrashed& ProcessCrashed::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_ProcessCrashed_2eproto();  return *default_instance_;
+}
+
+ProcessCrashed* ProcessCrashed::default_instance_ = NULL;
+
+ProcessCrashed* ProcessCrashed::New() const {
+  return new ProcessCrashed;
+}
+
+void ProcessCrashed::Clear() {
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    pid_ = GOOGLE_ULONGLONG(0);
+    signal_ = 0u;
+  }
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+  mutable_unknown_fields()->Clear();
+}
+
+bool ProcessCrashed::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+  ::google::protobuf::uint32 tag;
+  while ((tag = input->ReadTag()) != 0) {
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // required uint64 pid = 1;
+      case 1: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &pid_)));
+          _set_bit(0);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(16)) goto parse_signal;
+        break;
+      }
+      
+      // required uint32 signal = 2;
+      case 2: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_signal:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &signal_)));
+          _set_bit(1);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectAtEnd()) return true;
+        break;
+      }
+      
+      default: {
+      handle_uninterpreted:
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          return true;
+        }
+        DO_(::google::protobuf::internal::WireFormat::SkipField(
+              input, tag, mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+  return true;
+#undef DO_
+}
+
+void ProcessCrashed::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // required uint64 pid = 1;
+  if (_has_bit(0)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->pid(), output);
+  }
+  
+  // required uint32 signal = 2;
+  if (_has_bit(1)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->signal(), output);
+  }
+  
+  if (!unknown_fields().empty()) {
+    ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+        unknown_fields(), output);
+  }
+}
+
+::google::protobuf::uint8* ProcessCrashed::SerializeWithCachedSizesToArray(
+    ::google::protobuf::uint8* target) const {
+  // required uint64 pid = 1;
+  if (_has_bit(0)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(1, this->pid(), target);
+  }
+  
+  // required uint32 signal = 2;
+  if (_has_bit(1)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(2, this->signal(), target);
+  }
+  
+  if (!unknown_fields().empty()) {
+    target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+        unknown_fields(), target);
+  }
+  return target;
+}
+
+int ProcessCrashed::ByteSize() const {
+  int total_size = 0;
+  
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    // required uint64 pid = 1;
+    if (has_pid()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->pid());
+    }
+    
+    // required uint32 signal = 2;
+    if (has_signal()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->signal());
+    }
+    
+  }
+  if (!unknown_fields().empty()) {
+    total_size +=
+      ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+        unknown_fields());
+  }
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void ProcessCrashed::MergeFrom(const ::google::protobuf::Message& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  const ProcessCrashed* source =
+    ::google::protobuf::internal::dynamic_cast_if_available<const ProcessCrashed*>(
+      &from);
+  if (source == NULL) {
+    ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+  } else {
+    MergeFrom(*source);
+  }
+}
+
+void ProcessCrashed::MergeFrom(const ProcessCrashed& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    if (from._has_bit(0)) {
+      set_pid(from.pid());
+    }
+    if (from._has_bit(1)) {
+      set_signal(from.signal());
+    }
+  }
+  mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+}
+
+void ProcessCrashed::CopyFrom(const ::google::protobuf::Message& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void ProcessCrashed::CopyFrom(const ProcessCrashed& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool ProcessCrashed::IsInitialized() const {
+  if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false;
+  
+  return true;
+}
+
+void ProcessCrashed::Swap(ProcessCrashed* other) {
+  if (other != this) {
+    std::swap(pid_, other->pid_);
+    std::swap(signal_, other->signal_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
+::google::protobuf::Metadata ProcessCrashed::GetMetadata() const {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::Metadata metadata;
+  metadata.descriptor = ProcessCrashed_descriptor_;
+  metadata.reflection = ProcessCrashed_reflection_;
+  return metadata;
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace messages
+}  // namespace castor
+
+// @@protoc_insertion_point(global_scope)
diff --git a/castor/messages/ProcessCrashed.pb.h b/castor/messages/ProcessCrashed.pb.h
new file mode 100644
index 0000000000000000000000000000000000000000..1dbac5418b731ad52096672908e81efb1c5a25e3
--- /dev/null
+++ b/castor/messages/ProcessCrashed.pb.h
@@ -0,0 +1,191 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: ProcessCrashed.proto
+
+#ifndef PROTOBUF_ProcessCrashed_2eproto__INCLUDED
+#define PROTOBUF_ProcessCrashed_2eproto__INCLUDED
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+#if GOOGLE_PROTOBUF_VERSION < 2003000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers.  Please update
+#error your headers.
+#endif
+#if 2003000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers.  Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_message_reflection.h>
+// @@protoc_insertion_point(includes)
+
+namespace castor {
+namespace messages {
+
+// Internal implementation detail -- do not call these.
+void  protobuf_AddDesc_ProcessCrashed_2eproto();
+void protobuf_AssignDesc_ProcessCrashed_2eproto();
+void protobuf_ShutdownFile_ProcessCrashed_2eproto();
+
+class ProcessCrashed;
+
+// ===================================================================
+
+class ProcessCrashed : public ::google::protobuf::Message {
+ public:
+  ProcessCrashed();
+  virtual ~ProcessCrashed();
+  
+  ProcessCrashed(const ProcessCrashed& from);
+  
+  inline ProcessCrashed& operator=(const ProcessCrashed& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  
+  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+    return _unknown_fields_;
+  }
+  
+  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+    return &_unknown_fields_;
+  }
+  
+  static const ::google::protobuf::Descriptor* descriptor();
+  static const ProcessCrashed& default_instance();
+  
+  void Swap(ProcessCrashed* other);
+  
+  // implements Message ----------------------------------------------
+  
+  ProcessCrashed* New() const;
+  void CopyFrom(const ::google::protobuf::Message& from);
+  void MergeFrom(const ::google::protobuf::Message& from);
+  void CopyFrom(const ProcessCrashed& from);
+  void MergeFrom(const ProcessCrashed& from);
+  void Clear();
+  bool IsInitialized() const;
+  
+  int ByteSize() const;
+  bool MergePartialFromCodedStream(
+      ::google::protobuf::io::CodedInputStream* input);
+  void SerializeWithCachedSizes(
+      ::google::protobuf::io::CodedOutputStream* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  int GetCachedSize() const { return _cached_size_; }
+  private:
+  void SharedCtor();
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  public:
+  
+  ::google::protobuf::Metadata GetMetadata() const;
+  
+  // nested types ----------------------------------------------------
+  
+  // accessors -------------------------------------------------------
+  
+  // required uint64 pid = 1;
+  inline bool has_pid() const;
+  inline void clear_pid();
+  static const int kPidFieldNumber = 1;
+  inline ::google::protobuf::uint64 pid() const;
+  inline void set_pid(::google::protobuf::uint64 value);
+  
+  // required uint32 signal = 2;
+  inline bool has_signal() const;
+  inline void clear_signal();
+  static const int kSignalFieldNumber = 2;
+  inline ::google::protobuf::uint32 signal() const;
+  inline void set_signal(::google::protobuf::uint32 value);
+  
+  // @@protoc_insertion_point(class_scope:castor.messages.ProcessCrashed)
+ private:
+  ::google::protobuf::UnknownFieldSet _unknown_fields_;
+  mutable int _cached_size_;
+  
+  ::google::protobuf::uint64 pid_;
+  ::google::protobuf::uint32 signal_;
+  friend void  protobuf_AddDesc_ProcessCrashed_2eproto();
+  friend void protobuf_AssignDesc_ProcessCrashed_2eproto();
+  friend void protobuf_ShutdownFile_ProcessCrashed_2eproto();
+  
+  ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
+  
+  // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
+  inline bool _has_bit(int index) const {
+    return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;
+  }
+  inline void _set_bit(int index) {
+    _has_bits_[index / 32] |= (1u << (index % 32));
+  }
+  inline void _clear_bit(int index) {
+    _has_bits_[index / 32] &= ~(1u << (index % 32));
+  }
+  
+  void InitAsDefaultInstance();
+  static ProcessCrashed* default_instance_;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+// ProcessCrashed
+
+// required uint64 pid = 1;
+inline bool ProcessCrashed::has_pid() const {
+  return _has_bit(0);
+}
+inline void ProcessCrashed::clear_pid() {
+  pid_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(0);
+}
+inline ::google::protobuf::uint64 ProcessCrashed::pid() const {
+  return pid_;
+}
+inline void ProcessCrashed::set_pid(::google::protobuf::uint64 value) {
+  _set_bit(0);
+  pid_ = value;
+}
+
+// required uint32 signal = 2;
+inline bool ProcessCrashed::has_signal() const {
+  return _has_bit(1);
+}
+inline void ProcessCrashed::clear_signal() {
+  signal_ = 0u;
+  _clear_bit(1);
+}
+inline ::google::protobuf::uint32 ProcessCrashed::signal() const {
+  return signal_;
+}
+inline void ProcessCrashed::set_signal(::google::protobuf::uint32 value) {
+  _set_bit(1);
+  signal_ = value;
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace messages
+}  // namespace castor
+
+#ifndef SWIG
+namespace google {
+namespace protobuf {
+
+
+}  // namespace google
+}  // namespace protobuf
+#endif  // SWIG
+
+// @@protoc_insertion_point(global_scope)
+
+#endif  // PROTOBUF_ProcessCrashed_2eproto__INCLUDED
diff --git a/castor/messages/ProcessCrashed.proto b/castor/messages/ProcessCrashed.proto
new file mode 100644
index 0000000000000000000000000000000000000000..542b71b463535ef02009c65381ee390dc7519601
--- /dev/null
+++ b/castor/messages/ProcessCrashed.proto
@@ -0,0 +1,26 @@
+// 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
+
+package castor.messages;
+
+message ProcessCrashed {
+  required uint64 pid = 1;
+  required uint32 signal = 2;
+}
diff --git a/castor/messages/ProcessExited.pb.cc b/castor/messages/ProcessExited.pb.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a5537411c0651be923643edb77d6f4e8cfb8b670
--- /dev/null
+++ b/castor/messages/ProcessExited.pb.cc
@@ -0,0 +1,348 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+
+#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+#include "ProcessExited.pb.h"
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format_lite_inl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+
+namespace castor {
+namespace messages {
+
+namespace {
+
+const ::google::protobuf::Descriptor* ProcessExited_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+  ProcessExited_reflection_ = NULL;
+
+}  // namespace
+
+
+void protobuf_AssignDesc_ProcessExited_2eproto() {
+  protobuf_AddDesc_ProcessExited_2eproto();
+  const ::google::protobuf::FileDescriptor* file =
+    ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
+      "ProcessExited.proto");
+  GOOGLE_CHECK(file != NULL);
+  ProcessExited_descriptor_ = file->message_type(0);
+  static const int ProcessExited_offsets_[2] = {
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessExited, pid_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessExited, exitcode_),
+  };
+  ProcessExited_reflection_ =
+    new ::google::protobuf::internal::GeneratedMessageReflection(
+      ProcessExited_descriptor_,
+      ProcessExited::default_instance_,
+      ProcessExited_offsets_,
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessExited, _has_bits_[0]),
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ProcessExited, _unknown_fields_),
+      -1,
+      ::google::protobuf::DescriptorPool::generated_pool(),
+      ::google::protobuf::MessageFactory::generated_factory(),
+      sizeof(ProcessExited));
+}
+
+namespace {
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
+inline void protobuf_AssignDescriptorsOnce() {
+  ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
+                 &protobuf_AssignDesc_ProcessExited_2eproto);
+}
+
+void protobuf_RegisterTypes(const ::std::string&) {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+    ProcessExited_descriptor_, &ProcessExited::default_instance());
+}
+
+}  // namespace
+
+void protobuf_ShutdownFile_ProcessExited_2eproto() {
+  delete ProcessExited::default_instance_;
+  delete ProcessExited_reflection_;
+}
+
+void protobuf_AddDesc_ProcessExited_2eproto() {
+  static bool already_here = false;
+  if (already_here) return;
+  already_here = true;
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
+    "\n\023ProcessExited.proto\022\017castor.messages\"."
+    "\n\rProcessExited\022\013\n\003pid\030\001 \002(\004\022\020\n\010exitcode"
+    "\030\002 \002(\005", 86);
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
+    "ProcessExited.proto", &protobuf_RegisterTypes);
+  ProcessExited::default_instance_ = new ProcessExited();
+  ProcessExited::default_instance_->InitAsDefaultInstance();
+  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_ProcessExited_2eproto);
+}
+
+// Force AddDescriptors() to be called at static initialization time.
+struct StaticDescriptorInitializer_ProcessExited_2eproto {
+  StaticDescriptorInitializer_ProcessExited_2eproto() {
+    protobuf_AddDesc_ProcessExited_2eproto();
+  }
+} static_descriptor_initializer_ProcessExited_2eproto_;
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int ProcessExited::kPidFieldNumber;
+const int ProcessExited::kExitcodeFieldNumber;
+#endif  // !_MSC_VER
+
+ProcessExited::ProcessExited()
+  : ::google::protobuf::Message() {
+  SharedCtor();
+}
+
+void ProcessExited::InitAsDefaultInstance() {
+}
+
+ProcessExited::ProcessExited(const ProcessExited& from)
+  : ::google::protobuf::Message() {
+  SharedCtor();
+  MergeFrom(from);
+}
+
+void ProcessExited::SharedCtor() {
+  _cached_size_ = 0;
+  pid_ = GOOGLE_ULONGLONG(0);
+  exitcode_ = 0;
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+ProcessExited::~ProcessExited() {
+  SharedDtor();
+}
+
+void ProcessExited::SharedDtor() {
+  if (this != default_instance_) {
+  }
+}
+
+void ProcessExited::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* ProcessExited::descriptor() {
+  protobuf_AssignDescriptorsOnce();
+  return ProcessExited_descriptor_;
+}
+
+const ProcessExited& ProcessExited::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_ProcessExited_2eproto();  return *default_instance_;
+}
+
+ProcessExited* ProcessExited::default_instance_ = NULL;
+
+ProcessExited* ProcessExited::New() const {
+  return new ProcessExited;
+}
+
+void ProcessExited::Clear() {
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    pid_ = GOOGLE_ULONGLONG(0);
+    exitcode_ = 0;
+  }
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+  mutable_unknown_fields()->Clear();
+}
+
+bool ProcessExited::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+  ::google::protobuf::uint32 tag;
+  while ((tag = input->ReadTag()) != 0) {
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // required uint64 pid = 1;
+      case 1: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &pid_)));
+          _set_bit(0);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(16)) goto parse_exitcode;
+        break;
+      }
+      
+      // required int32 exitcode = 2;
+      case 2: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_exitcode:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &exitcode_)));
+          _set_bit(1);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectAtEnd()) return true;
+        break;
+      }
+      
+      default: {
+      handle_uninterpreted:
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          return true;
+        }
+        DO_(::google::protobuf::internal::WireFormat::SkipField(
+              input, tag, mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+  return true;
+#undef DO_
+}
+
+void ProcessExited::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // required uint64 pid = 1;
+  if (_has_bit(0)) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->pid(), output);
+  }
+  
+  // required int32 exitcode = 2;
+  if (_has_bit(1)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->exitcode(), output);
+  }
+  
+  if (!unknown_fields().empty()) {
+    ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+        unknown_fields(), output);
+  }
+}
+
+::google::protobuf::uint8* ProcessExited::SerializeWithCachedSizesToArray(
+    ::google::protobuf::uint8* target) const {
+  // required uint64 pid = 1;
+  if (_has_bit(0)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(1, this->pid(), target);
+  }
+  
+  // required int32 exitcode = 2;
+  if (_has_bit(1)) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->exitcode(), target);
+  }
+  
+  if (!unknown_fields().empty()) {
+    target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+        unknown_fields(), target);
+  }
+  return target;
+}
+
+int ProcessExited::ByteSize() const {
+  int total_size = 0;
+  
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    // required uint64 pid = 1;
+    if (has_pid()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->pid());
+    }
+    
+    // required int32 exitcode = 2;
+    if (has_exitcode()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->exitcode());
+    }
+    
+  }
+  if (!unknown_fields().empty()) {
+    total_size +=
+      ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+        unknown_fields());
+  }
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void ProcessExited::MergeFrom(const ::google::protobuf::Message& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  const ProcessExited* source =
+    ::google::protobuf::internal::dynamic_cast_if_available<const ProcessExited*>(
+      &from);
+  if (source == NULL) {
+    ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+  } else {
+    MergeFrom(*source);
+  }
+}
+
+void ProcessExited::MergeFrom(const ProcessExited& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    if (from._has_bit(0)) {
+      set_pid(from.pid());
+    }
+    if (from._has_bit(1)) {
+      set_exitcode(from.exitcode());
+    }
+  }
+  mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+}
+
+void ProcessExited::CopyFrom(const ::google::protobuf::Message& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void ProcessExited::CopyFrom(const ProcessExited& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool ProcessExited::IsInitialized() const {
+  if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false;
+  
+  return true;
+}
+
+void ProcessExited::Swap(ProcessExited* other) {
+  if (other != this) {
+    std::swap(pid_, other->pid_);
+    std::swap(exitcode_, other->exitcode_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
+::google::protobuf::Metadata ProcessExited::GetMetadata() const {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::Metadata metadata;
+  metadata.descriptor = ProcessExited_descriptor_;
+  metadata.reflection = ProcessExited_reflection_;
+  return metadata;
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace messages
+}  // namespace castor
+
+// @@protoc_insertion_point(global_scope)
diff --git a/castor/messages/ProcessExited.pb.h b/castor/messages/ProcessExited.pb.h
new file mode 100644
index 0000000000000000000000000000000000000000..a135427d1f03f92998bf73e6640633507949598e
--- /dev/null
+++ b/castor/messages/ProcessExited.pb.h
@@ -0,0 +1,191 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: ProcessExited.proto
+
+#ifndef PROTOBUF_ProcessExited_2eproto__INCLUDED
+#define PROTOBUF_ProcessExited_2eproto__INCLUDED
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+#if GOOGLE_PROTOBUF_VERSION < 2003000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers.  Please update
+#error your headers.
+#endif
+#if 2003000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers.  Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_message_reflection.h>
+// @@protoc_insertion_point(includes)
+
+namespace castor {
+namespace messages {
+
+// Internal implementation detail -- do not call these.
+void  protobuf_AddDesc_ProcessExited_2eproto();
+void protobuf_AssignDesc_ProcessExited_2eproto();
+void protobuf_ShutdownFile_ProcessExited_2eproto();
+
+class ProcessExited;
+
+// ===================================================================
+
+class ProcessExited : public ::google::protobuf::Message {
+ public:
+  ProcessExited();
+  virtual ~ProcessExited();
+  
+  ProcessExited(const ProcessExited& from);
+  
+  inline ProcessExited& operator=(const ProcessExited& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  
+  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+    return _unknown_fields_;
+  }
+  
+  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+    return &_unknown_fields_;
+  }
+  
+  static const ::google::protobuf::Descriptor* descriptor();
+  static const ProcessExited& default_instance();
+  
+  void Swap(ProcessExited* other);
+  
+  // implements Message ----------------------------------------------
+  
+  ProcessExited* New() const;
+  void CopyFrom(const ::google::protobuf::Message& from);
+  void MergeFrom(const ::google::protobuf::Message& from);
+  void CopyFrom(const ProcessExited& from);
+  void MergeFrom(const ProcessExited& from);
+  void Clear();
+  bool IsInitialized() const;
+  
+  int ByteSize() const;
+  bool MergePartialFromCodedStream(
+      ::google::protobuf::io::CodedInputStream* input);
+  void SerializeWithCachedSizes(
+      ::google::protobuf::io::CodedOutputStream* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  int GetCachedSize() const { return _cached_size_; }
+  private:
+  void SharedCtor();
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  public:
+  
+  ::google::protobuf::Metadata GetMetadata() const;
+  
+  // nested types ----------------------------------------------------
+  
+  // accessors -------------------------------------------------------
+  
+  // required uint64 pid = 1;
+  inline bool has_pid() const;
+  inline void clear_pid();
+  static const int kPidFieldNumber = 1;
+  inline ::google::protobuf::uint64 pid() const;
+  inline void set_pid(::google::protobuf::uint64 value);
+  
+  // required int32 exitcode = 2;
+  inline bool has_exitcode() const;
+  inline void clear_exitcode();
+  static const int kExitcodeFieldNumber = 2;
+  inline ::google::protobuf::int32 exitcode() const;
+  inline void set_exitcode(::google::protobuf::int32 value);
+  
+  // @@protoc_insertion_point(class_scope:castor.messages.ProcessExited)
+ private:
+  ::google::protobuf::UnknownFieldSet _unknown_fields_;
+  mutable int _cached_size_;
+  
+  ::google::protobuf::uint64 pid_;
+  ::google::protobuf::int32 exitcode_;
+  friend void  protobuf_AddDesc_ProcessExited_2eproto();
+  friend void protobuf_AssignDesc_ProcessExited_2eproto();
+  friend void protobuf_ShutdownFile_ProcessExited_2eproto();
+  
+  ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
+  
+  // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
+  inline bool _has_bit(int index) const {
+    return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;
+  }
+  inline void _set_bit(int index) {
+    _has_bits_[index / 32] |= (1u << (index % 32));
+  }
+  inline void _clear_bit(int index) {
+    _has_bits_[index / 32] &= ~(1u << (index % 32));
+  }
+  
+  void InitAsDefaultInstance();
+  static ProcessExited* default_instance_;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+// ProcessExited
+
+// required uint64 pid = 1;
+inline bool ProcessExited::has_pid() const {
+  return _has_bit(0);
+}
+inline void ProcessExited::clear_pid() {
+  pid_ = GOOGLE_ULONGLONG(0);
+  _clear_bit(0);
+}
+inline ::google::protobuf::uint64 ProcessExited::pid() const {
+  return pid_;
+}
+inline void ProcessExited::set_pid(::google::protobuf::uint64 value) {
+  _set_bit(0);
+  pid_ = value;
+}
+
+// required int32 exitcode = 2;
+inline bool ProcessExited::has_exitcode() const {
+  return _has_bit(1);
+}
+inline void ProcessExited::clear_exitcode() {
+  exitcode_ = 0;
+  _clear_bit(1);
+}
+inline ::google::protobuf::int32 ProcessExited::exitcode() const {
+  return exitcode_;
+}
+inline void ProcessExited::set_exitcode(::google::protobuf::int32 value) {
+  _set_bit(1);
+  exitcode_ = value;
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace messages
+}  // namespace castor
+
+#ifndef SWIG
+namespace google {
+namespace protobuf {
+
+
+}  // namespace google
+}  // namespace protobuf
+#endif  // SWIG
+
+// @@protoc_insertion_point(global_scope)
+
+#endif  // PROTOBUF_ProcessExited_2eproto__INCLUDED
diff --git a/castor/messages/ProcessExited.proto b/castor/messages/ProcessExited.proto
new file mode 100644
index 0000000000000000000000000000000000000000..5c8d8ff31f224918edc18755122e60faf6456635
--- /dev/null
+++ b/castor/messages/ProcessExited.proto
@@ -0,0 +1,26 @@
+// 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
+
+package castor.messages;
+
+message ProcessExited {
+  required uint64 pid = 1;
+  required int32 exitcode = 2;
+}
diff --git a/castor/tape/tapeserver/daemon/CMakeLists.txt b/castor/tape/tapeserver/daemon/CMakeLists.txt
index 3df2074699ccf0463135b545db656789af9eb07d..e21e27ca2a69d4314e8990ef2197d2aa526c6927 100644
--- a/castor/tape/tapeserver/daemon/CMakeLists.txt
+++ b/castor/tape/tapeserver/daemon/CMakeLists.txt
@@ -25,6 +25,7 @@ add_library(castorTapeServerDaemon
   MigrationTaskInjector.cpp
   DataTransferSession.cpp
   ProcessForker.cpp
+  ProcessForkerConnectionHandler.cpp
   ProcessForkerProxy.cpp
   ProcessForkerMsgType.cpp
   ProcessForkerProxySocket.cpp
diff --git a/castor/tape/tapeserver/daemon/DataTransferSession.cpp b/castor/tape/tapeserver/daemon/DataTransferSession.cpp
index 190cfcc617179b8e0b8e0d102f8dff728021f07e..2b3826dd2b31d9d5634754442741c478ce5a2a71 100644
--- a/castor/tape/tapeserver/daemon/DataTransferSession.cpp
+++ b/castor/tape/tapeserver/daemon/DataTransferSession.cpp
@@ -21,35 +21,28 @@
  * @author Castor Dev team, castor-dev@cern.ch
  *****************************************************************************/
 
-#include "castor/tape/tapeserver/daemon/DataTransferSession.hpp"
 #include "castor/log/LogContext.hpp"
-#include "castor/tape/tapeserver/exception/Exception.hpp"
-#include "castor/tape/tapeserver/client/ClientProxy.hpp"
-#include "log.h"
-#include "stager_client_commandline.h"
-#include "castor/tape/utils/utils.hpp"
 #include "castor/System.hpp"
-#include "castor/tape/tapeserver/SCSI/Device.hpp"
-#include "castor/tape/tapeserver/drive/Drive.hpp"
-#include "castor/tape/tapeserver/daemon/RecallTaskInjector.hpp"
-#include "castor/tape/tapeserver/daemon/RecallReportPacker.hpp"
-#include "castor/tape/tapeserver/daemon/TapeWriteSingleThread.hpp"
+#include "castor/tape/tapeserver/client/ClientProxy.hpp"
+#include "castor/tape/tapeserver/daemon/DataTransferSession.hpp"
 #include "castor/tape/tapeserver/daemon/DiskReadThreadPool.hpp"
-#include "castor/tape/tapeserver/daemon/MigrationTaskInjector.hpp"
 #include "castor/tape/tapeserver/daemon/DiskWriteThreadPool.hpp"
-#include "castor/tape/tapeserver/daemon/TapeServerReporter.hpp"
+#include "castor/tape/tapeserver/daemon/MigrationTaskInjector.hpp"
+#include "castor/tape/tapeserver/daemon/RecallReportPacker.hpp"
+#include "castor/tape/tapeserver/daemon/RecallTaskInjector.hpp"
+#include "castor/tape/tapeserver/daemon/TapeWriteSingleThread.hpp"
 #include "castor/tape/tapeserver/daemon/TapeReadSingleThread.hpp"
-#include "castor/tape/tapeserver/daemon/DataTransferSession.hpp"
+#include "castor/tape/tapeserver/daemon/TapeServerReporter.hpp"
+#include "castor/tape/tapeserver/drive/Drive.hpp"
+#include "castor/tape/tapeserver/exception/Exception.hpp"
+#include "castor/tape/tapeserver/SCSI/Device.hpp"
+#include "castor/tape/utils/utils.hpp"
+#include "h/log.h"
 #include "h/serrno.h"
+#include "h/stager_client_commandline.h"
+
 #include <google/protobuf/stubs/common.h>
 #include <memory>
-#include <zmq.h>
-
-//------------------------------------------------------------------------------
-// m_zmqContext
-//------------------------------------------------------------------------------
-void *castor::tape::tapeserver::daemon::DataTransferSession::m_zmqContext =
-  NULL;
 
 //------------------------------------------------------------------------------
 //Constructor
@@ -57,14 +50,15 @@ void *castor::tape::tapeserver::daemon::DataTransferSession::m_zmqContext =
 castor::tape::tapeserver::daemon::DataTransferSession::DataTransferSession(
     const std::string & hostname,
     const legacymsg::RtcpJobRqstMsgBody & clientRequest, 
-    castor::log::Logger& logger, System::virtualWrapper & sysWrapper,
+    castor::log::Logger & log,
+    System::virtualWrapper & sysWrapper,
     const utils::DriveConfig & driveConfig,
     castor::legacymsg::RmcProxy & rmc,
     castor::messages::TapeserverProxy & initialProcess,
-    castor::server::ProcessCap &capUtils,
+    castor::server::ProcessCap & capUtils,
     const CastorConf & castorConf): 
     m_request(clientRequest),
-    m_logger(logger),
+    m_log(log),
     m_clientProxy(clientRequest),
     m_sysWrapper(sysWrapper),
     m_driveConfig(driveConfig),
@@ -88,7 +82,7 @@ castor::tape::tapeserver::daemon::DataTransferSession::DataTransferSession(
 int castor::tape::tapeserver::daemon::DataTransferSession::execute()
  {
   // 1) Prepare the logging environment
-  log::LogContext lc(m_logger);
+  log::LogContext lc(m_log);
   // Create a sticky thread name, which will be overridden by the other threads
   lc.pushOrReplace(log::Param("thread", "mainThread"));
   log::LogContext::ScopedParam sp01(lc, log::Param("clientHost", m_request.clientHost));
@@ -187,11 +181,12 @@ int castor::tape::tapeserver::daemon::DataTransferSession::executeRead(log::LogC
     RecallReportPacker rrp(m_clientProxy,
         m_castorConf.tapebridgeBulkRequestMigrationMaxFiles,
         lc);
+
     // If we talk to a command line client, we do not batch report
     if (tapegateway::READ_TP == m_volInfo.clientType) {
       rrp.disableBulk();
     }
-    TaskWatchDog<detail::Recall> watchdog(m_intialProcess,rrp,m_logger);
+    TaskWatchDog<detail::Recall> watchdog(m_intialProcess, rrp , m_log);
     
     RecallMemoryManager mm(m_castorConf.rtcopydNbBufs, m_castorConf.rtcopydBufsz,lc);
     TapeServerReporter tsr(m_intialProcess, m_driveConfig, 
@@ -485,33 +480,15 @@ castor::tape::tapeserver::daemon::DataTransferSession::findDrive(const utils::Dr
   }
 }
 
-//------------------------------------------------------------------------------
-// getZmqContext
-//------------------------------------------------------------------------------
-void *castor::tape::tapeserver::daemon::DataTransferSession::getZmqContext() {
-  // Create the ZMQ context if it does not already exist
-  if(NULL == m_zmqContext) {
-    const int sizeOfIOThreadPoolForZMQ = 1;
-    m_zmqContext = zmq_init(sizeOfIOThreadPoolForZMQ);
-    if(NULL == m_zmqContext) {
-      char message[100];
-      sstrerror_r(errno, message, sizeof(message));
-      castor::exception::Exception ex;
-      ex.getMessage() << "Failed to instantiate ZMQ context: " << message;
-      throw ex;
-    }
-  }
-
-  return m_zmqContext;
-}
-
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
-castor::tape::tapeserver::daemon::DataTransferSession::~DataTransferSession(){
-  if(NULL != m_zmqContext) {
-    zmq_term(m_zmqContext);
-    m_zmqContext = NULL;
+castor::tape::tapeserver::daemon::DataTransferSession::~DataTransferSession()
+  throw () {
+  try {
+    google::protobuf::ShutdownProtobufLibrary();
+  } catch(...) {
+    m_log(LOG_ERR, "google::protobuf::ShutdownProtobufLibrary() threw an"
+      " unexpected exception");
   }
-  google::protobuf::ShutdownProtobufLibrary();
 }
diff --git a/castor/tape/tapeserver/daemon/DataTransferSession.hpp b/castor/tape/tapeserver/daemon/DataTransferSession.hpp
index f31bae59fd825ec7126c0832ba16537402ad1102..fa950dfe2a3a7f5cb7ce678d9dd7483ddfabde64 100644
--- a/castor/tape/tapeserver/daemon/DataTransferSession.hpp
+++ b/castor/tape/tapeserver/daemon/DataTransferSession.hpp
@@ -92,11 +92,15 @@ namespace daemon {
       // Additions for tapeserverd
       uint32_t tapeserverdDiskThreads;
     };
-    /** Constructor */
+    /**
+     * Constructor.
+     *
+     * @param log Object representing the API of the CASTOR logging system.
+     */
     DataTransferSession(
       const std::string & hostname,
       const legacymsg::RtcpJobRqstMsgBody & clientRequest, 
-      castor::log::Logger & logger,
+      castor::log::Logger & log,
       System::virtualWrapper & sysWrapper,
       const utils::DriveConfig & driveConfig,
       castor::legacymsg::RmcProxy & rmc,
@@ -109,24 +113,19 @@ namespace daemon {
     std::string getVid() { return m_volInfo.vid; }
     
     /**
-     * Return the global shared zmq context for the data-transfer session
-     * THIS FUNCTION SHALL ONLY BE CALLED IN THE FORKED PROCESS
-     *
-     * @return The global shared zmq context for the data-transfer session.
+     * Destructor.
      */
-    static void *getZmqContext();
-    
-    ~DataTransferSession();
+    ~DataTransferSession() throw();
 
   private:
 
+    legacymsg::RtcpJobRqstMsgBody m_request;
+
     /**
-     * The global shared zmq context for the date-transfer session.
+     * Object representing the API of the CASTOR logging system.
      */
-    static void *m_zmqContext;
+    castor::log::Logger & m_log;
 
-    legacymsg::RtcpJobRqstMsgBody m_request;
-    castor::log::Logger & m_logger;
     client::ClientProxy m_clientProxy;
     client::ClientProxy::VolumeInfo m_volInfo;
     System::virtualWrapper & m_sysWrapper;
diff --git a/castor/tape/tapeserver/daemon/ProcessForker.cpp b/castor/tape/tapeserver/daemon/ProcessForker.cpp
index 54d10462e54fb73126d0c176b05d93abb99bceea..887cd1b95f11e27c14e7b374b63438429ff65068 100644
--- a/castor/tape/tapeserver/daemon/ProcessForker.cpp
+++ b/castor/tape/tapeserver/daemon/ProcessForker.cpp
@@ -22,16 +22,23 @@
  *****************************************************************************/
 
 #include "castor/exception/Exception.hpp"
+#include "castor/legacymsg/RmcProxyTcpIp.hpp"
 #include "castor/messages/ForkCleaner.pb.h"
 #include "castor/messages/ForkDataTransfer.pb.h"
 #include "castor/messages/ForkLabel.pb.h"
 #include "castor/messages/ForkSucceeded.pb.h"
-#include "castor/messages/Status.pb.h"
+#include "castor/messages/ProcessCrashed.pb.h"
+#include "castor/messages/ProcessExited.pb.h"
 #include "castor/messages/StopProcessForker.pb.h"
+#include "castor/messages/TapeserverProxyZmq.hpp"
+#include "castor/tape/tapeserver/daemon/Constants.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForker.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForkerUtils.hpp"
+#include "castor/tape/utils/DriveConfig.hpp"
+#include "castor/tape/utils/SmartZmqContext.hpp"
 #include "castor/utils/SmartArrayPtr.hpp"
+#include "castor/utils/utils.hpp"
 #include "h/serrno.h"
 
 #include <errno.h>
@@ -44,21 +51,41 @@
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-castor::tape::tapeserver::daemon::ProcessForker::ProcessForker(log::Logger &log,
-  const int socketFd) throw(): m_log(log), m_socketFd(socketFd) {
+castor::tape::tapeserver::daemon::ProcessForker::ProcessForker(
+  log::Logger &log,
+  const int cmdSocket,
+  const int reaperSocket,
+  const std::string &hostName) throw():
+  m_log(log),
+  m_cmdSocket(cmdSocket),
+  m_reaperSocket(reaperSocket),
+  m_hostName(hostName) {
 }
 
 //------------------------------------------------------------------------------
 // destructor
 //------------------------------------------------------------------------------
 castor::tape::tapeserver::daemon::ProcessForker::~ProcessForker() throw() {
-  if(-1 == close(m_socketFd)) {
-    char message[100];
-    sstrerror_r(errno, message, sizeof(message));
-    log::Param params[] = {log::Param("socketFd", m_socketFd),
-      log::Param("message", message)};
-    m_log(LOG_ERR, "Failed to close file-descriptor of ProcessForkerSocket",
-      params);
+  closeCmdReceiverSocket();
+}
+
+//------------------------------------------------------------------------------
+// closeCmdReceiverSocket
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForker::closeCmdReceiverSocket()
+  throw() {
+  if(-1 != m_cmdSocket) {
+    std::list<log::Param> params;
+    params.push_back(log::Param("cmdSocket", m_cmdSocket));
+    if(-1 == close(m_cmdSocket)) {
+      char message[100];
+      sstrerror_r(errno, message, sizeof(message));
+      params.push_back(log::Param("message", message));
+      m_log(LOG_ERR, "Failed to close command receiver socket",
+        params);
+    } else {
+      m_log(LOG_INFO, "Closed command receiver socket", params);
+    }
   }
 }
 
@@ -68,6 +95,7 @@ castor::tape::tapeserver::daemon::ProcessForker::~ProcessForker() throw() {
 void castor::tape::tapeserver::daemon::ProcessForker::execute() {
   try {
     while(handleEvents()) {
+      reapZombies();
     }
   } catch(castor::exception::Exception &ne) {
     castor::exception::Exception ex;
@@ -94,7 +122,7 @@ bool castor::tape::tapeserver::daemon::ProcessForker::thereIsAPendingMsg() {
 
   // Call poll() in orer to see if there is any data to be read
   struct pollfd fds;
-  fds.fd = m_socketFd;
+  fds.fd = m_cmdSocket;
   fds.events = POLLIN;
   fds.revents = 0;
   const int timeout = 100; // Timeout in milliseconds
@@ -136,7 +164,7 @@ bool castor::tape::tapeserver::daemon::ProcessForker::thereIsAPendingMsg() {
 bool castor::tape::tapeserver::daemon::ProcessForker::handleMsg() {
   ProcessForkerFrame frame;
   try {
-    frame = ProcessForkerUtils::readFrame(m_socketFd);
+    frame = ProcessForkerUtils::readFrame(m_cmdSocket);
   } catch(castor::exception::Exception &ne) {
     log::Param params[] = {log::Param("message", ne.getMessage().str())};
     m_log(LOG_ERR, "Failed to handle message", params);
@@ -161,31 +189,31 @@ bool castor::tape::tapeserver::daemon::ProcessForker::handleMsg() {
     log::Param("message", ex.getMessage().str());
     m_log(LOG_ERR, "ProcessForker::dispatchMsgHandler() threw an exception",
       params);
-    messages::Status msg;
-    msg.set_status(ex.code());
+    messages::Exception msg;
+    msg.set_code(ex.code());
     msg.set_message(ex.getMessage().str());
-    ProcessForkerUtils::writeFrame(m_socketFd, msg);
+    ProcessForkerUtils::writeFrame(m_cmdSocket, msg);
     return true; // The main event loop should continue
   } catch(std::exception &se) {
     log::Param("message", se.what());
     m_log(LOG_ERR, "ProcessForker::dispatchMsgHandler() threw an exception",
       params);
-    messages::Status msg;
-    msg.set_status(SEINTERNAL);
+    messages::Exception msg;
+    msg.set_code(SEINTERNAL);
     msg.set_message(se.what());
-    ProcessForkerUtils::writeFrame(m_socketFd, msg);
+    ProcessForkerUtils::writeFrame(m_cmdSocket, msg);
     return true; // The main event loop should continue
   } catch(...) {
     m_log(LOG_ERR,
       "ProcessForker::dispatchMsgHandler() threw an unknown exception");
-    messages::Status msg;
-    msg.set_status(SEINTERNAL);
+    messages::Exception msg;
+    msg.set_code(SEINTERNAL);
     msg.set_message("Caught and unknown and unexpected exception");
-    ProcessForkerUtils::writeFrame(m_socketFd, msg);
+    ProcessForkerUtils::writeFrame(m_cmdSocket, msg);
     return true; // The main event loop should continue
   }
 
-  ProcessForkerUtils::writeFrame(m_socketFd, result.reply);
+  ProcessForkerUtils::writeFrame(m_cmdSocket, result.reply);
   {
     log::Param params[] = {
       log::Param("payloadType",
@@ -252,8 +280,8 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
       params);
 
     // Create and return the result of handling the incomming request
-    messages::Status reply;
-    reply.set_status(SEINTERNAL);
+    messages::Exception reply;
+    reply.set_code(SEINTERNAL);
     reply.set_message("Failed to fork cleaner session for tape drive");
     MsgHandlerResult result;
     result.continueMainEventLoop = true;
@@ -278,13 +306,14 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
 
   // Else this is the child process
   } else {
+    closeCmdReceiverSocket();
     // TO BE DONE
     exit(0);
   }
 }
 
 //------------------------------------------------------------------------------
-// handleDataTransferMsg
+// handleForkDataTransferMsg
 //------------------------------------------------------------------------------
 castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult 
   castor::tape::tapeserver::daemon::ProcessForker::handleForkDataTransferMsg(
@@ -313,8 +342,8 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
       params);
 
     // Create and return the result of handling the incomming request
-    messages::Status reply;
-    reply.set_status(SEINTERNAL);
+    messages::Exception reply;
+    reply.set_code(SEINTERNAL);
     reply.set_message("Failed to fork data-transfer session for tape drive");
     MsgHandlerResult result;
     result.continueMainEventLoop = true;
@@ -326,9 +355,6 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
     log::Param params[] = {log::Param("pid", forkRc)};
     m_log(LOG_INFO, "ProcessForker forked data-transfer session", params);
 
-    // TO BE DONE
-    waitpid(forkRc, NULL, 0);
-
     // Create and return the result of handling the incomming request
     messages::ForkSucceeded reply;
     reply.set_pid(forkRc);
@@ -339,8 +365,22 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
 
   // Else this is the child process
   } else {
-    // TO BE DONE
-    exit(0);
+    closeCmdReceiverSocket();
+
+    try {
+      exit(runDataTransferSession(rqst));
+    } catch(castor::exception::Exception &ne) {
+      log::Param params[] = {log::Param("message", ne.getMessage().str())};
+      m_log(LOG_ERR, "Failed to run data-transfer session", params);
+    } catch(std::exception &ne) {
+      log::Param params[] = {log::Param("message", ne.what())};
+      m_log(LOG_ERR, "Failed to run data-transfer session", params);
+    } catch(...) {
+      log::Param params[] = {log::Param("message",
+        "Caught an unknown exception")};
+      m_log(LOG_ERR, "Failed to run data-transfer session", params);
+    }
+    exit(1);
   }
 }
 
@@ -373,8 +413,8 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
       params);
 
     // Create and return the result of handling the incomming request
-    messages::Status reply;
-    reply.set_status(SEINTERNAL);
+    messages::Exception reply;
+    reply.set_code(SEINTERNAL);
     reply.set_message("Failed to fork label session for tape drive");
     MsgHandlerResult result;
     result.continueMainEventLoop = true;
@@ -399,6 +439,8 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
 
   // Else this is the child process
   } else {
+    closeCmdReceiverSocket();
+
     // TO BE DONE
     exit(0);
   }
@@ -420,11 +462,325 @@ castor::tape::tapeserver::daemon::ProcessForker::MsgHandlerResult
   m_log(LOG_INFO, "Gracefully stopping ProcessForker", params);
 
   // Create and return the result of handling the incomming request
-  messages::Status reply;
-  reply.set_status(0);
+  messages::Exception reply;
+  reply.set_code(0);
   reply.set_message("");
   MsgHandlerResult result;
   result.continueMainEventLoop = false;
   ProcessForkerUtils::serializePayload(result.reply, reply);
   return result;
 }
+
+//------------------------------------------------------------------------------
+// runDataTransferSession
+//------------------------------------------------------------------------------
+int castor::tape::tapeserver::daemon::ProcessForker::runDataTransferSession(
+  const messages::ForkDataTransfer &rqst) {
+  const utils::DriveConfig driveConfig = getDriveConfig(rqst);
+
+  std::list<log::Param> params;
+  params.push_back(log::Param("unitName", driveConfig.unitName));
+  m_log(LOG_INFO, "Data-transfer child-process started", params);
+
+  const legacymsg::RtcpJobRqstMsgBody vdqmJob = getVdqmJob(rqst);
+
+  castor::server::ProcessCap capUtils;
+  const DataTransferSession::CastorConf dataTransferConfig =
+    getDataTransferConfig(rqst);
+
+  const int netTimeout = 10; // Timeout in seconds
+  legacymsg::RmcProxyTcpIp rmc(m_log, netTimeout);
+
+  const int sizeOfIOThreadPoolForZMQ = 1;
+  utils::SmartZmqContext
+    zmqContext(instantiateZmqContext(sizeOfIOThreadPoolForZMQ));
+
+  messages::TapeserverProxyZmq tapeserver(m_log,
+    TAPE_SERVER_INTERNAL_LISTENING_PORT, netTimeout, zmqContext.get());
+
+  castor::tape::System::realWrapper sysWrapper;
+
+  // This try bloc will allow us to send a failure notification to the client
+  // if we fail before the DataTransferSession has an opportunity to do so.
+  std::auto_ptr<DataTransferSession> dataTransferSession;
+  try {
+    dataTransferSession.reset(new DataTransferSession (
+      m_hostName,
+      vdqmJob,
+      m_log,
+      sysWrapper,
+      driveConfig,
+      rmc,
+      tapeserver,
+      capUtils,
+      dataTransferConfig));
+  } catch (castor::exception::Exception & ex) {
+    try {
+      client::ClientProxy cl(vdqmJob);
+      client::ClientInterface::RequestReport rep;
+      cl.reportEndOfSessionWithError(ex.getMessageValue(), ex.code(), rep);
+    } catch (...) {
+      params.push_back(log::Param("errorMessage", ex.getMessageValue()));
+      params.push_back(log::Param("errorCode", ex.code()));
+      m_log(LOG_ERR, "Failed to notify the client of the failed session"
+        " when setting up the data-transfer session", params);
+    }
+    throw;
+  } catch (...) {
+    try {
+      m_log(LOG_ERR, "Got non castor exception error while constructing"
+        " data-transfer session", params);
+      client::ClientProxy cl(vdqmJob);
+      client::ClientInterface::RequestReport rep;
+      cl.reportEndOfSessionWithError(
+       "Non-Castor exception when setting up the data-transfer session",
+         SEINTERNAL, rep);
+    } catch (...) {
+      params.push_back(log::Param("errorMessage",
+        "Non-Castor exception when setting up the data-transfer session"));
+      m_log(LOG_ERR, "Failed to notify the client of the failed session"
+        " when setting up the data-transfer session", params);
+    }
+    throw;
+  }
+  m_log(LOG_INFO, "Going to execute data-transfer session");
+  return dataTransferSession->execute();
+}
+
+//------------------------------------------------------------------------------
+// getDriveConfig
+//------------------------------------------------------------------------------
+castor::tape::utils::DriveConfig
+  castor::tape::tapeserver::daemon::ProcessForker::getDriveConfig(
+  const messages::ForkDataTransfer &msg) {
+  utils::DriveConfig config;
+  config.unitName = msg.unitname();
+  config.dgn = msg.dgn();
+  config.devFilename = msg.devfilename();
+  for(int i=0; i < msg.density_size(); i++) {
+    config.densities.push_back(msg.density(i));
+  }
+  config.librarySlot = msg.libraryslot();
+  config.devType = msg.devtype();
+
+  return config;
+}
+
+//------------------------------------------------------------------------------
+// getDataTransferConfig
+//------------------------------------------------------------------------------
+castor::tape::tapeserver::daemon::DataTransferSession::CastorConf
+  castor::tape::tapeserver::daemon::ProcessForker::getDataTransferConfig(
+  const messages::ForkDataTransfer &msg) {
+  DataTransferSession::CastorConf config;
+  config.rtcopydBufsz = msg.memblocksize();
+  config.rtcopydNbBufs = msg.nbmemblocks();
+  config.tapeBadMIRHandlingRepair = msg.badmirhandling();
+  config.tapebridgeBulkRequestMigrationMaxBytes =
+    msg.bulkrequestmigrationmaxbytes();
+  config.tapebridgeBulkRequestMigrationMaxFiles =
+    msg.bulkrequestmigrationmaxfiles();
+  config.tapebridgeBulkRequestRecallMaxBytes = msg.bulkrequestrecallmaxbytes();
+  config.tapebridgeBulkRequestRecallMaxFiles = msg.bulkrequestrecallmaxfiles();
+  config.tapebridgeMaxBytesBeforeFlush = msg.maxbytesbeforeflush();
+  config.tapebridgeMaxFilesBeforeFlush = msg.maxfilesbeforeflush();
+  config.tapeserverdDiskThreads = msg.diskthreadpoolsize();
+
+  return config;
+}
+
+//------------------------------------------------------------------------------
+// getVdqmJob
+//------------------------------------------------------------------------------
+castor::legacymsg::RtcpJobRqstMsgBody
+  castor::tape::tapeserver::daemon::ProcessForker::getVdqmJob(
+  const messages::ForkDataTransfer &msg) {
+  castor::legacymsg::RtcpJobRqstMsgBody job;
+  job.volReqId = msg.mounttransactionid();
+  job.clientPort = msg.clientport();
+  job.clientEuid = msg.clienteuid();
+  job.clientEgid = msg.clientegid();
+  castor::utils::copyString(job.clientHost, msg.clienthost());
+  castor::utils::copyString(job.dgn, msg.dgn());
+  castor::utils::copyString(job.driveUnit, msg.unitname());
+  castor::utils::copyString(job.clientUserName, msg.clientusername());
+
+  return job;
+}
+
+//------------------------------------------------------------------------------
+// instantiateZmqContext
+//------------------------------------------------------------------------------
+void *castor::tape::tapeserver::daemon::ProcessForker::instantiateZmqContext(
+  const int sizeOfIOThreadPoolForZMQ) {
+  void *const zmqContext = zmq_init(sizeOfIOThreadPoolForZMQ);
+  if(NULL == zmqContext) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to instantiate ZMQ context: " << message;
+    throw ex;
+  }
+  std::ostringstream contextAddr;
+  contextAddr << std::hex << zmqContext;
+  log::Param params[] = {log::Param("contextAddr", contextAddr.str())};
+  m_log(LOG_INFO, "ProcessForker instantiated a ZMQ context", params);
+
+  return zmqContext;
+}
+
+//------------------------------------------------------------------------------
+// reapZombies
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForker::reapZombies() throw() {
+  pid_t pid = 0;
+  int waitpidStat = 0;
+  while (0 < (pid = waitpid(-1, &waitpidStat, WNOHANG))) {
+    handleReapedZombie(pid, waitpidStat);
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleReapedZombie
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForker::handleReapedZombie(
+  const pid_t pid, const int waitpidStat) throw() {
+  try {
+    logChildProcessTerminated(pid, waitpidStat);
+    notifyTapeDaemonOfTerminatedProcess(pid, waitpidStat);
+  } catch(castor::exception::Exception &ex) {
+    log::Param params[] = {log::Param("message", ex.getMessage().str())};
+    m_log(LOG_ERR, "Failed to handle reaped zombie", params);
+  } catch(std::exception &se) {
+    log::Param params[] = {log::Param("message", se.what())};
+    m_log(LOG_ERR, "Failed to handle reaped zombie", params);
+  } catch(...) {
+    log::Param params[] = {
+      log::Param("message", "Caught an unknown exception")};
+    m_log(LOG_ERR, "Failed to handle reaped zombie", params);
+  }
+} 
+
+//------------------------------------------------------------------------------
+// logChildProcessTerminated
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForker::logChildProcessTerminated(
+  const pid_t pid, const int waitpidStat) throw() {
+  std::list<log::Param> params;
+  params.push_back(log::Param("terminatedPid", pid));
+
+  if(WIFEXITED(waitpidStat)) {
+    params.push_back(log::Param("WEXITSTATUS", WEXITSTATUS(waitpidStat)));
+  }
+
+  if(WIFSIGNALED(waitpidStat)) {
+    params.push_back(log::Param("WTERMSIG", WTERMSIG(waitpidStat)));
+  }
+
+  if(WCOREDUMP(waitpidStat)) {
+    params.push_back(log::Param("WCOREDUMP", "true"));
+  } else {
+    params.push_back(log::Param("WCOREDUMP", "false"));
+  }
+
+  if(WIFSTOPPED(waitpidStat)) {
+    params.push_back(log::Param("WSTOPSIG", WSTOPSIG(waitpidStat)));
+  }
+
+  if(WIFCONTINUED(waitpidStat)) {
+    params.push_back(log::Param("WIFCONTINUED", "true"));
+  } else {
+    params.push_back(log::Param("WIFCONTINUED", "false"));
+  }
+
+  m_log(LOG_INFO, "ProcessForker child process terminated", params);
+}
+
+//------------------------------------------------------------------------------
+// notifyTapeDaemonOfTerminatedProcess
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForker::
+  notifyTapeDaemonOfTerminatedProcess(const pid_t pid, const int waitpidStat) {
+  try {
+    if(WIFEXITED(waitpidStat)) {
+      notifyTapeDaemonOfExitedProcess(pid, waitpidStat);
+    } else if(WIFSIGNALED(waitpidStat)) {
+      notifyTapeDaemonOfCrashedProcess(pid, waitpidStat);
+    } else {
+      castor::exception::Exception ex;
+      ex.getMessage() << "Process died of unknown causes" << pid;
+      throw ex;
+    }
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to notify TapeDaemon of process termination"
+      ": pid=" << pid << ": " << ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// notifyTapeDaemonOfExitedProcess
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForker::
+  notifyTapeDaemonOfExitedProcess(const pid_t pid, const int waitpidStat) {
+  try {
+    messages::ProcessExited msg;
+    msg.set_pid(pid);
+    msg.set_exitcode(WEXITSTATUS(waitpidStat));
+
+    log::Param params[] = {log::Param("pid", msg.pid()),
+      log::Param("exitCode", msg.exitcode())};
+    m_log(LOG_INFO, "Notifying TapeDaemon of process exit", params);
+
+    ProcessForkerUtils::writeFrame(m_reaperSocket, msg);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to notify TapeDaemon of process exit: " <<
+      ne.getMessage().str();
+    throw ex;
+  } catch(std::exception &se) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to notify TapeDaemon of process exit: " <<
+      se.what();
+    throw ex;
+  } catch(...) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to notify TapeDaemon of process exit: "
+      "Caught an unknown exception";
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// notifyTapeDaemonOfCrashedProcess
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForker::
+  notifyTapeDaemonOfCrashedProcess(const pid_t pid, const int waitpidStat) {
+  try {
+    messages::ProcessCrashed msg;
+    msg.set_pid(pid);
+    msg.set_signal(WTERMSIG(waitpidStat));
+
+    log::Param params[] = {log::Param("pid", msg.pid()),
+      log::Param("signal", msg.signal())};
+    m_log(LOG_INFO, "Notifying TapeDaemon of process crash", params);
+
+    ProcessForkerUtils::writeFrame(m_reaperSocket, msg);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to notify TapeDaemon of process crash: " <<
+      ne.getMessage().str();
+    throw ex;
+  } catch(std::exception &se) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to notify TapeDaemon of process crash: " <<
+      se.what();
+    throw ex;
+  } catch(...) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to notify TapeDaemon of process crash: "
+      "Caught an unknown exception";
+    throw ex;
+  }
+}
diff --git a/castor/tape/tapeserver/daemon/ProcessForker.hpp b/castor/tape/tapeserver/daemon/ProcessForker.hpp
index 0c77e277d2aaef778581202ae9641e3e3886cba1..2e902f6121e56b4c3758e7b66493dc21abb9924c 100644
--- a/castor/tape/tapeserver/daemon/ProcessForker.hpp
+++ b/castor/tape/tapeserver/daemon/ProcessForker.hpp
@@ -24,6 +24,8 @@
 #pragma once
 
 #include "castor/log/Logger.hpp"
+#include "castor/messages/ForkDataTransfer.pb.h"
+#include "castor/tape/tapeserver/daemon/DataTransferSession.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForkerFrame.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp"
 
@@ -47,10 +49,16 @@ public:
    * client.  The destructor of this class will close the file-descriptor.
    *
    * @param log Object representing the API of the CASTOR logging system.
-   * @param socketFd The file-descriptor of the socket used to communicate with
-   * the client.
+   * @param cmdSocket The file-descriptor of the socket used to receive commands
+   * from the ProcessForker proxy.
+   * @param reaperSocket The file-descriptor of the socket used to notify the
+   * TapeDaemon parent process of the termination of a process forked by the
+   * ProcessForker.
+   * @param hostName The name of the host on which the tapeserverd daemon is
+   * running.
    */
-  ProcessForker(log::Logger &log, const int socketFd) throw();
+  ProcessForker(log::Logger &log, const int cmdSocket, const int reaperSocket,
+    const std::string &hostName) throw();
 
   /**
    * Destructor.
@@ -79,9 +87,27 @@ private:
   log::Logger &m_log;
 
   /**
-   * The file-descriptor of the socket used to communicate with the client.
+   * The file-descriptor of the socket used to receive commands from the
+   * ProcessForker proxy.
    */
-  const int m_socketFd;
+  const int m_cmdSocket;
+
+  /**
+   * The file-descriptor of the socket used to notify the TapeDaemon parent
+   * process of the termination of a process forked by the ProcessForker.
+   */
+  const int m_reaperSocket;
+
+  /**
+   * The name of the host on which the tapeserverd daemon is running.
+   */ 
+  const std::string m_hostName;
+
+  /**
+   * Idempotent method that closes the socket used for receving commands
+   * from the ProcessForker proxy.
+   */
+  void closeCmdReceiverSocket() throw();
 
   /**
    * Structure defining the result of a message handler.
@@ -163,6 +189,104 @@ private:
    */
   MsgHandlerResult handleForkCleanerMsg(const ProcessForkerFrame &frame);
 
+  /**
+   * Runs the data-transfer session.  This method is to be called within the
+   * child process responsible for running the data-transfer session.
+   *
+   * @param rqst The ForkDataTransfer message.
+   * @return The value to be used when exiting the child process.
+   */
+  int runDataTransferSession(const messages::ForkDataTransfer &rqst);
+
+  /**
+   * Gets the drve configuration information from the specified ForkDataTransfer
+   * message.
+   *
+   * @param msg The ForkDataTransfer message.
+   * @return The drive configuration.
+   */
+  utils::DriveConfig getDriveConfig(const messages::ForkDataTransfer &msg);
+
+  /**
+   * Gets the configuration of the data-transfer session from the specified
+   * ForkDataTransfer message.
+   *
+   * @param msg The ForkDataTransfer message.
+   * @return The configuration of the data-transfer session.
+   */
+  castor::tape::tapeserver::daemon::DataTransferSession::CastorConf
+    getDataTransferConfig(const messages::ForkDataTransfer &msg);
+
+  /**
+   * Gets the VDQM job from the specified ForkDataTransfer message.
+   *
+   * @param msg The ForkDataTransfer message.
+   * @return The VDQM job.
+   */
+  castor::legacymsg::RtcpJobRqstMsgBody getVdqmJob(
+    const messages::ForkDataTransfer &msg);
+
+  /**
+   * Instantiates a ZMQ context.
+   *
+   * @param sizeOfIOThreadPoolForZMQ The size of the IO thread pool to be used
+   * by ZMQ.
+   * @return The ZMQ context.
+   */
+  void *instantiateZmqContext(const int sizeOfIOThreadPoolForZMQ);
+
+  /**
+   * Reaps any zombie processes.
+   */
+  void reapZombies() throw();
+
+  /**
+   * Handles the specified reaped zombie.
+   *
+   * @param pid The process ID of the reaped zombie.
+   * @param waitpidStat The status information given by a call to waitpid().
+   */
+  void handleReapedZombie(const pid_t pid, const int waitpidStat) throw();
+
+  /**
+   * Logs the fact that the specified child process has terminated.
+   *
+   * @param pid The process ID of the child process.
+   * @param waitpidStat The status information given by a call to waitpid().
+   */
+  void logChildProcessTerminated(const pid_t pid, const int waitpidStat)
+    throw();
+
+  /**
+   * Notifies the TapeDaemon parent process that a child process of the
+   * ProcessForker has terminated.
+   *
+   * @param pid The process ID of the child process.
+   * @param waitpidStat The status information given by a call to waitpid().
+   */
+  void notifyTapeDaemonOfTerminatedProcess(const pid_t pid,
+    const int waitpidStat);
+
+  /**
+   * Notifies the TapeDaemon parent process that a child process of the
+   * ProcessForker exited.
+   *
+   * @param pid The process ID of the child process.
+   * @param waitpidStat The status information given by a call to waitpid().
+   */
+  void notifyTapeDaemonOfExitedProcess(const pid_t pid,
+    const int waitpidStat);
+
+  /**
+   * Notifies the TapeDaemon parent process that a child process of the
+   * ProcessForker crashed.
+   *
+   * @param pid The process ID of the child process.
+   * @param waitpidStat The status information given by a call to waitpid().
+   */
+  void notifyTapeDaemonOfCrashedProcess(const pid_t pid,
+    const int waitpidStat);
+
 }; // class ProcessForker
 
 } // namespace daemon
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.cpp b/castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ace3c928d36fb2528d5007c27c6729ed5e9bf755
--- /dev/null
+++ b/castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.cpp
@@ -0,0 +1,488 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.hpp"
+#include "castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp"
+#include "castor/tape/tapeserver/daemon/ProcessForkerUtils.hpp"
+#include "h/common.h"
+#include "h/serrno.h"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  ProcessForkerConnectionHandler(
+  const int fd,
+  reactor::ZMQReactor &reactor,
+  log::Logger &log,
+  DriveCatalogue &driveCatalogue,
+  const std::string &hostName,
+  legacymsg::VdqmProxy &vdqm) throw():
+  m_fd(fd),
+  m_reactor(reactor),
+  m_log(log),
+  m_driveCatalogue(driveCatalogue),
+  m_hostName(hostName),
+  m_vdqm(vdqm),
+  m_netTimeout(1) // Timeout in seconds
+{
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  ~ProcessForkerConnectionHandler() throw() {
+  log::Param params[] = {log::Param("fd", m_fd)};
+  m_log(LOG_DEBUG, "Closing incoming connection from the ProcessForker",
+    params);
+  close(m_fd);
+}
+
+//------------------------------------------------------------------------------
+// getName
+//------------------------------------------------------------------------------
+std::string castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  getName() const throw() {
+  return "ProcessForkerConnectionHandler";
+}
+
+//------------------------------------------------------------------------------
+// fillPollFd
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  fillPollFd(zmq_pollitem_t &fd) throw() {
+  fd.fd = m_fd;
+  fd.events = ZMQ_POLLIN;
+  fd.revents = 0;
+  fd.socket = NULL;
+}
+
+//------------------------------------------------------------------------------
+// handleEvent
+//------------------------------------------------------------------------------
+bool castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleEvent(const zmq_pollitem_t &fd)  {
+  logConnectionEvent(fd);
+
+  checkHandleEventFd(fd.fd);
+
+  handleMsg();
+
+  return false; // Ask reactor to keep this handler registered
+}
+
+//------------------------------------------------------------------------------
+// logConnectionEvent 
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  logConnectionEvent(const zmq_pollitem_t &fd)  {
+  log::Param params[] = {
+  log::Param("fd", fd.fd),
+  log::Param("ZMQ_POLLIN", fd.revents & ZMQ_POLLIN ? "true" : "false"),
+  log::Param("ZMQ_POLLOUT", fd.revents & ZMQ_POLLOUT ? "true" : "false"),
+  log::Param("ZMQ_POLLERR", fd.revents & ZMQ_POLLERR ? "true" : "false")};
+  m_log(LOG_DEBUG, "I/O event on incoming ProcessForker connection", params);
+}
+
+//------------------------------------------------------------------------------
+// checkHandleEventFd
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  checkHandleEventFd(const int fd)  {
+  if(m_fd != fd) {
+    castor::exception::Exception ex;
+    ex.getMessage() <<
+      "ProcessForkerConnectionHandler passed wrong file descriptor"
+      ": expected=" << m_fd << " actual=" << fd;
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleMsg
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleMsg() {
+  ProcessForkerFrame frame;
+  try {
+    frame = ProcessForkerUtils::readFrame(m_fd);
+  } catch(castor::exception::Exception &ne) {
+    log::Param params[] = {log::Param("message", ne.getMessage().str())};
+    m_log(LOG_ERR, "Failed to handle message", params);
+    sleep(1); // Sleep a moment to avoid going into a tight error loop
+  }
+
+  log::Param params[] = {
+    log::Param("type", ProcessForkerMsgType::toString(frame.type)),
+    log::Param("len", frame.payload.length())};
+  m_log(LOG_INFO, "ProcessForkerConnectionHandler handling a message", params);
+
+  dispatchMsgHandler(frame);
+}
+
+//------------------------------------------------------------------------------
+// dispatchMsgHandler
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  dispatchMsgHandler(const ProcessForkerFrame &frame) {
+  switch(frame.type) {
+  case ProcessForkerMsgType::MSG_PROCESSCRASHED:
+    return handleProcessCrashedMsg(frame);
+  case ProcessForkerMsgType::MSG_PROCESSEXITED:
+    return handleProcessExitedMsg(frame);
+  default:
+    {
+      castor::exception::Exception ex;
+      ex.getMessage() << "Failed to dispatch message handler"
+        ": Unexpected message type"
+        ": type=" << ProcessForkerMsgType::toString(frame.type);
+      throw ex;
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleProcessCrashedMsg
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleProcessCrashedMsg(const ProcessForkerFrame &frame) {
+  try {
+    // Parse the message
+    messages::ProcessCrashed msg;
+    ProcessForkerUtils::parsePayload(frame, msg);
+
+    // Log the contents of the message
+    std::list<log::Param> params;
+    params.push_back(log::Param("pid", msg.pid()));
+    params.push_back(log::Param("signal", msg.signal()));
+    m_log(LOG_INFO,
+      "ProcessForkerConnectionHandler handling ProcessCrashed message", params);
+
+    DriveCatalogueEntry *const drive = m_driveCatalogue.findDrive(msg.pid());
+    dispatchCrashedProcessHandler(*drive, msg);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle ProcessCrashed message: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// dispatchCrashedProcessHandler
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  dispatchCrashedProcessHandler(DriveCatalogueEntry &drive,
+  const messages::ProcessCrashed &msg) {
+
+  switch(drive.getSessionType()) {
+  case DriveCatalogueEntry::SESSION_TYPE_DATATRANSFER:
+    return handleCrashedDataTransferSession(drive, msg);
+  case DriveCatalogueEntry::SESSION_TYPE_LABEL:
+    return handleCrashedLabelSession(drive, msg);
+  case DriveCatalogueEntry::SESSION_TYPE_CLEANER:
+    return handleCrashedCleanerSession(drive, msg);
+  default:
+    {
+      castor::exception::Exception ex;
+      ex.getMessage() << "Failed to dispatch handler for crashed process"
+        ": Unexpected session type: sessionType=" << drive.getSessionType();
+      throw ex;
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleCrashedDataTransferSession
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleCrashedDataTransferSession(DriveCatalogueEntry &drive,
+  const messages::ProcessCrashed &msg) {
+  std::list<log::Param> params;
+  params.push_back(log::Param("pid", msg.pid()));
+  params.push_back(log::Param("signal", msg.signal()));
+
+  try {
+    drive.sessionFailed();
+    m_log(LOG_WARNING, "Data-transfer session failed", params);
+    drive.toBeCleaned();
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle crashed data-transfer session: " << 
+    ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleCrashedCleanerSession
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleCrashedCleanerSession(DriveCatalogueEntry &drive,
+  const messages::ProcessCrashed &msg) {
+  std::list<log::Param> params;
+  params.push_back(log::Param("pid", msg.pid()));
+  params.push_back(log::Param("signal", msg.signal()));
+
+  try {
+    drive.sessionFailed();
+    m_log(LOG_WARNING, "Cleaner session failed", params);
+    setDriveDownInVdqm(msg.pid(), drive.getConfig());
+  }  catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle crashed cleaner session: " << 
+    ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleCrashedLabelSession 
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleCrashedLabelSession(DriveCatalogueEntry &drive,
+  const messages::ProcessCrashed &msg) {
+  std::list<log::Param> params;
+  params.push_back(log::Param("pid", msg.pid()));
+  params.push_back(log::Param("signal", msg.signal()));
+
+  try {
+    drive.sessionFailed();
+    m_log(LOG_WARNING, "Label session failed", params);
+    setDriveDownInVdqm(msg.pid(), drive.getConfig());
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle crashed label session: " <<
+    ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleProcessExitedMsg
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleProcessExitedMsg(const ProcessForkerFrame &frame) {
+  try {
+    // Parse the message
+    messages::ProcessExited msg;
+    ProcessForkerUtils::parsePayload(frame, msg);
+
+    // Log the contents of the message
+    std::list<log::Param> params;
+    params.push_back(log::Param("pid", msg.pid()));
+    params.push_back(log::Param("exitCode", msg.exitcode()));
+    m_log(LOG_INFO, 
+      "ProcessForkerConnectionHandler handling ProcessExited message", params);
+
+    DriveCatalogueEntry *const drive = m_driveCatalogue.findDrive(msg.pid());
+    dispatchExitedProcessHandler(*drive, msg);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle ProcessCrashed message: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// dispatchExitedProcessHandler
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  dispatchExitedProcessHandler(DriveCatalogueEntry &drive,
+  const messages::ProcessExited &msg) {
+
+  switch(drive.getSessionType()) {
+  case DriveCatalogueEntry::SESSION_TYPE_DATATRANSFER:
+    return handleExitedDataTransferSession(drive, msg);
+  case DriveCatalogueEntry::SESSION_TYPE_LABEL:
+    return handleExitedLabelSession(drive, msg);
+  case DriveCatalogueEntry::SESSION_TYPE_CLEANER:
+    return handleExitedCleanerSession(drive, msg);
+  default:
+    {
+      castor::exception::Exception ex;
+      ex.getMessage() << "Failed to dispatch handler for exited process"
+        ": Unexpected session type: sessionType=" << drive.getSessionType();
+      throw ex;
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleExitedDataTransferSession
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleExitedDataTransferSession(DriveCatalogueEntry &drive,
+  const messages::ProcessExited &msg) {
+  std::list<log::Param> params;
+  params.push_back(log::Param("pid", msg.pid()));
+  params.push_back(log::Param("exitCode", msg.exitcode()));
+
+  try {
+    if(0 == msg.exitcode()) {
+      const std::string vid = drive.getVid();
+      drive.sessionSucceeded();
+      m_log(LOG_INFO, "Data-transfer session succeeded", params);
+      requestVdqmToReleaseDrive(drive.getConfig(), msg.pid());
+      notifyVdqmTapeUnmounted(drive.getConfig(), vid, msg.pid());
+    } else {
+      drive.sessionFailed();
+      m_log(LOG_WARNING, "Data-transfer session failed", params);
+      setDriveDownInVdqm(msg.pid(), drive.getConfig());
+    }
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle exited data-transfer session: " << 
+    ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleExitedCleanerSession
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleExitedCleanerSession(DriveCatalogueEntry &drive,
+  const messages::ProcessExited &msg) {
+  std::list<log::Param> params;
+  params.push_back(log::Param("pid", msg.pid()));
+  params.push_back(log::Param("exitCode", msg.exitcode()));
+
+  try {
+    if(0 == msg.exitcode()) {
+      drive.sessionSucceeded();
+      m_log(LOG_INFO, "Cleaner session succeeded", params);
+      const std::string &vid = drive.getVid();
+      requestVdqmToReleaseDrive(drive.getConfig(), msg.pid());
+      notifyVdqmTapeUnmounted(drive.getConfig(), vid, msg.pid());
+    } else {
+      drive.sessionFailed();
+      m_log(LOG_WARNING, "Cleaner session failed", params);
+      setDriveDownInVdqm(msg.pid(), drive.getConfig());
+    }
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle exited cleaner session: " << 
+    ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleExitedLabelSession 
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  handleExitedLabelSession(DriveCatalogueEntry &drive,
+  const messages::ProcessExited &msg) {
+  std::list<log::Param> params;
+  params.push_back(log::Param("pid", msg.pid()));
+  params.push_back(log::Param("exitCode", msg.exitcode()));
+
+  try {
+    if(0 == msg.exitcode()) {
+      drive.sessionSucceeded();
+      m_log(LOG_INFO, "Label session succeeded", params);
+    } else {
+      drive.sessionFailed();
+      m_log(LOG_WARNING, "Label session failed", params);
+      setDriveDownInVdqm(msg.pid(), drive.getConfig());
+    }
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle exited label session: " <<
+    ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// requestVdqmToReleaseDrive
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  requestVdqmToReleaseDrive(const utils::DriveConfig &driveConfig,
+  const pid_t pid) {
+  std::list<log::Param> params;
+  try {
+    const bool forceUnmount = true;
+
+    params.push_back(log::Param("pid", pid));
+    params.push_back(log::Param("unitName", driveConfig.unitName));
+    params.push_back(log::Param("dgn", driveConfig.dgn));
+    params.push_back(log::Param("forceUnmount", forceUnmount));
+
+    m_vdqm.releaseDrive(m_hostName, driveConfig.unitName, driveConfig.dgn,
+      forceUnmount, pid);
+    m_log(LOG_INFO, "Requested vdqm to release drive", params);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to request vdqm to release drive: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// notifyVdqmTapeUnmounted
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  notifyVdqmTapeUnmounted(const utils::DriveConfig &driveConfig,
+  const std::string &vid, const pid_t pid) {
+  try {
+    std::list<log::Param> params;
+    params.push_back(log::Param("pid", pid));
+    params.push_back(log::Param("unitName", driveConfig.unitName));
+    params.push_back(log::Param("vid", vid));
+    params.push_back(log::Param("dgn", driveConfig.dgn));
+
+    m_vdqm.tapeUnmounted(m_hostName, driveConfig.unitName, driveConfig.dgn,
+      vid);
+    m_log(LOG_INFO, "Notified vdqm that a tape was unmounted", params);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed notify vdqm that a tape was unmounted: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// setDriveDownInVdqm
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerConnectionHandler::
+  setDriveDownInVdqm(const pid_t pid, const utils::DriveConfig &driveConfig) {
+  std::list<log::Param> params;
+  params.push_back(log::Param("pid", pid));
+
+  try {
+    params.push_back(log::Param("unitName", driveConfig.unitName));
+    params.push_back(log::Param("dgn", driveConfig.dgn));
+
+    m_vdqm.setDriveDown(m_hostName, driveConfig.unitName, driveConfig.dgn);
+    m_log(LOG_INFO, "Set tape-drive down in vdqm", params);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to set tape-drive down in vdqm: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.hpp b/castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e55709a1308262ef4c5303c2eba3da47c878c613
--- /dev/null
+++ b/castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.hpp
@@ -0,0 +1,279 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+#include "castor/io/io.hpp"
+#include "castor/log/Logger.hpp"
+#include "castor/messages/ProcessCrashed.pb.h"
+#include "castor/messages/ProcessExited.pb.h"
+#include "castor/legacymsg/CommonMarshal.hpp"
+#include "castor/legacymsg/MessageHeader.hpp"
+#include "castor/legacymsg/VdqmProxy.hpp"
+#include "castor/tape/reactor/PollEventHandler.hpp"
+#include "castor/tape/reactor/ZMQReactor.hpp"
+#include "castor/tape/tapeserver/daemon/DriveCatalogue.hpp"
+#include "castor/tape/tapeserver/daemon/ProcessForkerFrame.hpp"
+
+namespace castor     {
+namespace tape       {
+namespace tapeserver {
+namespace daemon     {
+
+/**
+ * Handles the events of the incoming connection from the ProcessForker.
+ */
+class ProcessForkerConnectionHandler:
+  public reactor::ZMQPollEventHandler {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param fd The file descriptor of the incomming connection from the
+   * ProcessForker
+   * @param reactor The reactor with which this event handler is registered.
+   * @param log The object representing the API of the CASTOR logging system.
+   * @param driveCatalogue The catalogue of tape drives controlled by the tape
+   * server daemon.
+   * @param hostName The name of the host on which the tape-server daemon is
+   * running.
+   * @param vdqm Proxy object representing the vdqmd daemon.
+   */
+  ProcessForkerConnectionHandler(
+    const int fd,
+    reactor::ZMQReactor &reactor,
+    log::Logger &log,
+    DriveCatalogue &driveCatalogue,
+    const std::string &hostName,
+    legacymsg::VdqmProxy &vdqm) throw();
+
+  /**
+   * Destructor.
+   *
+   * Closes the incoming connection with the ProcessForker.
+   */
+  ~ProcessForkerConnectionHandler() 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
+   * zmq_poll().
+   */
+  void fillPollFd(zmq_pollitem_t &fd) throw();
+
+  /**
+   * Handles the specified event.
+   *
+   * @param fd The poll file-descriptor describing the event.
+   */
+  bool handleEvent(const zmq_pollitem_t &fd) ;
+
+private:
+
+  /**
+   * The file descriptor of the incomming connection from the ProcessForker.
+   */
+  const int m_fd;
+
+  /**
+   * The reactor with which this event handler is registered.
+   */
+  reactor::ZMQReactor &m_reactor;
+
+  /**
+   * The object representing the API of the CASTOR logging system.
+   */
+  log::Logger &m_log;
+
+  /**
+   * The catalogue of tape drives controlled by the tape server daemon.
+   */
+  DriveCatalogue &m_driveCatalogue;
+
+  /**
+   * The name of th ehost on which the tape-server daemon is running.
+   */
+  const std::string m_hostName;
+
+  /** 
+   * Proxy object representing the vdqmd daemon.
+   */
+  castor::legacymsg::VdqmProxy &m_vdqm;
+  
+  /**
+   * The timeout in seconds to be applied when performing network read and
+   * write operations.
+   */
+  const int m_netTimeout;
+
+  /**
+   * Logs the specifed IO event of the incoming ProcessForker connection.
+   */
+  void logConnectionEvent(const zmq_pollitem_t &fd);
+
+  /**
+   * Throws an exception if the specified file-descriptor is not that of the
+   * connection with the client.
+   */
+  void checkHandleEventFd(const int fd) ;
+
+  /**
+   * Handles an incoming message from the ProcessForker.
+   */
+  void handleMsg();
+
+  /**
+   * Dispatches the appropriate message handler for the message contained
+   * within the specified frame.
+   *
+   * @param frame The frame containing the message.
+   */
+  void dispatchMsgHandler(const ProcessForkerFrame &frame);
+
+  /**
+   * Handles the specified ProcessCrashedMsg.
+   *
+   * @param frame The frame containing the message.
+   */
+  void handleProcessCrashedMsg(const ProcessForkerFrame &frame);
+
+  /**
+   * Dispatches the appropriate handler for the specified crashed process.
+   *
+   * @param drive The drive associated with the crashed process.
+   * @param msg The ProcessCrashed message.
+   */
+  void dispatchCrashedProcessHandler(DriveCatalogueEntry &drive,
+    const messages::ProcessCrashed &msg);
+
+  /**
+   * Handles the specified crashed process.
+   *
+   * @param drive The drive associated with the crashed process.
+   * @param msg The ProcessCrashed message.
+   */
+  void handleCrashedDataTransferSession(DriveCatalogueEntry &drive,
+    const messages::ProcessCrashed &msg);
+
+  /**
+   * Handles the specified crashed process.
+   *
+   * @param drive The drive associated with the crashed process.
+   * @param msg The ProcessCrashed message.
+   */
+  void handleCrashedCleanerSession(DriveCatalogueEntry &drive,
+    const messages::ProcessCrashed &msg);
+
+  /**
+   * Handles the specified crashed process.
+   *
+   * @param drive The drive associated with the crashed process.
+   * @param msg The ProcessCrashed message.
+   */
+  void handleCrashedLabelSession(DriveCatalogueEntry &drive,
+    const messages::ProcessCrashed &msg);
+
+  /**
+   * Handles the specified ProcessExitedMsg.
+   *
+   * @param frame The frame containing the message.
+   */
+  void handleProcessExitedMsg(const ProcessForkerFrame &frame);
+
+  /**
+   * Dispatches the appropriate handler for the specified exited process.
+   *
+   * @param drive The drive associated with the exited process.
+   * @param msg The ProcessExited message.
+   */
+  void dispatchExitedProcessHandler(DriveCatalogueEntry &drive,
+    const messages::ProcessExited &msg);
+
+  /**
+   * Handles the specified exited process.
+   *
+   * @param drive The drive associated with the crashed process.
+   * @param msg The ProcessCrashed message.
+   */
+  void handleExitedDataTransferSession(DriveCatalogueEntry &drive,
+    const messages::ProcessExited &msg);
+  
+  /**
+   * Handles the specified exited process.
+   *
+   * @param drive The drive associated with the crashed process.
+   * @param msg The ProcessCrashed message.
+   */
+  void handleExitedCleanerSession(DriveCatalogueEntry &drive,
+    const messages::ProcessExited &msg);
+
+  /**
+   * Handles the specified exited process.
+   *
+   * @param drive The drive associated with the crashed process.
+   * @param msg The ProcessCrashed message.
+   */
+  void handleExitedLabelSession(DriveCatalogueEntry &drive,
+    const messages::ProcessExited &msg);
+
+  /** 
+   * Request the vdqmd daemon to release the tape drive associated with the
+   * session child-process with the specified process ID.
+   *  
+   * @param driveConfig The configuration of the tape drive.
+   * @param pid The process ID of the session child-process.
+   */
+  void requestVdqmToReleaseDrive(const utils::DriveConfig &driveConfig,
+    const pid_t pid);
+
+  /**
+   * Sets the state of the tape drive asscoiated with the specified
+   * child process to down within the vdqmd daemon.
+   *
+   * @param pid The process ID of the child process.
+   * @param driveConfig The configuration of the tape drive.
+   */
+  void setDriveDownInVdqm(const pid_t pid,
+    const utils::DriveConfig &driveConfig);
+
+  /** 
+   * Notifies the vdqm that the tape associated with the session child-process
+   * with the specified process ID has been unmounted.
+   *
+   * @param driveConfig The configuration of the tape drive.
+   * @param vid The identifier of the unmounted volume.
+   * @param pid The process ID of the session child-process.
+   */
+  void notifyVdqmTapeUnmounted(const utils::DriveConfig &driveConfig,
+    const std::string &vid, const pid_t pid);
+
+}; // class ProcessForkerConnectionHandler
+
+} // namespace daemon
+} // namespace tapeserver
+} // namespace tape
+} // namespace castor
+
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerMsgType.cpp b/castor/tape/tapeserver/daemon/ProcessForkerMsgType.cpp
index 6169849146a13bec1efa1fe0ed268a80b053e2b7..885b1a7763b0e5af660320ac239ebbb779e2caab 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerMsgType.cpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerMsgType.cpp
@@ -30,11 +30,13 @@ const char *castor::tape::tapeserver::daemon::ProcessForkerMsgType::
   toString(const Enum value) throw() {
   switch(value) {
   case MSG_NONE             : return "None";
-  case MSG_FORKCLEANER      : return "Cleaner";
+  case MSG_EXCEPTION        : return "Exception";
+  case MSG_FORKCLEANER      : return "ForkCleaner";
   case MSG_FORKDATATRANSFER : return "ForkDataTransfer";
   case MSG_FORKLABEL        : return "ForkLabel";
   case MSG_FORKSUCCEEDED    : return "ForkSucceeded";
-  case MSG_STATUS           : return "Status";
+  case MSG_PROCESSCRASHED   : return "ProcessCrashed";
+  case MSG_PROCESSEXITED    : return "ProcessExited";
   case MSG_STOPPROCESSFORKER: return "StopProcessForker";
   default                   : return "Unknown";
   }
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp b/castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp
index d296ca271b527ab1d4f63cc3a3902717af1e539e..9610f29511cecd6368514d6862e2ffe3fedadae0 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp
@@ -41,11 +41,13 @@ public:
    */
   enum Enum {
     MSG_NONE,
+    MSG_EXCEPTION,
     MSG_FORKCLEANER,
     MSG_FORKDATATRANSFER,
     MSG_FORKLABEL,
     MSG_FORKSUCCEEDED,
-    MSG_STATUS,
+    MSG_PROCESSCRASHED,
+    MSG_PROCESSEXITED,
     MSG_STOPPROCESSFORKER
   };
 
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerProxy.hpp b/castor/tape/tapeserver/daemon/ProcessForkerProxy.hpp
index ac674af09094f5db94b17e23f5d46c69e550e036..2b8df71561170327351b0beb324a2186a3d5eabb 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerProxy.hpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerProxy.hpp
@@ -23,7 +23,10 @@
 
 #pragma once
 
+#include "castor/legacymsg/RtcpJobRqstMsgBody.hpp"
 #include "castor/log/Logger.hpp"
+#include "castor/tape/tapeserver/daemon/DataTransferSession.hpp"
+#include "castor/tape/utils/DriveConfig.hpp"
 
 namespace castor     {
 namespace tape       {
@@ -52,18 +55,24 @@ public:
   /**
    * Forks a data-transfer session for the specified tape drive.
    *
-   * @param unitName The unit name of the tape drive.
+   * @param driveConfig The configuration of the tape drive.
+   * @param vdqmJob The job received from the vdqmd daemon.
+   * @param conf The configuration of the data-transfer session.
+   * @return The process identifier of the newly forked session.
    */
-  virtual void forkDataTransfer(const std::string &unitName) = 0;
+  virtual pid_t forkDataTransfer(const utils::DriveConfig &driveConfig,
+    const legacymsg::RtcpJobRqstMsgBody vdqmJob,
+    const DataTransferSession::CastorConf &conf) = 0;
 
   /**
    * Forks a label session for the specified tape drive.
    *
    * @param unitName The unit name of the tape drive.
    * @param vid The volume identifier of the tape.
+   * @return The process identifier of the newly forked session.
    */
-  virtual void forkLabel(const std::string &unitName, const std::string &vid) =
-    0;
+  virtual pid_t forkLabel(const std::string &unitName,
+    const std::string &vid) = 0;
 
   /**
    * Forks a cleaner session for the specified tape drive.
@@ -73,8 +82,9 @@ public:
    * tape in the drive if there is in fact a tape in the drive and its volume
    * identifier is known.  If the volume identifier is not known then this
    * parameter should be set to an empty string.
+   * @return The process identifier of the newly forked session.
    */
-  virtual void forkCleaner(const std::string &unitName,
+  virtual pid_t forkCleaner(const std::string &unitName,
     const std::string &vid) = 0;
 
 }; // class ProcessForkerProxy
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.cpp b/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.cpp
index 5b46f0a34b805487aaec3750f5e68d38d6968236..6c9a88d2c37d4bb24bd23c993947207ed74116bb 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.cpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.cpp
@@ -22,7 +22,6 @@
  *****************************************************************************/
 
 #include "castor/messages/ForkCleaner.pb.h"
-#include "castor/messages/ForkDataTransfer.pb.h"
 #include "castor/messages/ForkLabel.pb.h"
 #include "castor/messages/ForkSucceeded.pb.h"
 #include "castor/messages/StopProcessForker.pb.h"
@@ -74,12 +73,15 @@ void castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
 //------------------------------------------------------------------------------
 // forkDataTransfer
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
-  forkDataTransfer(const std::string &unitName) {
+pid_t castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
+  forkDataTransfer(const utils::DriveConfig &driveConfig,
+    const legacymsg::RtcpJobRqstMsgBody vdqmJob,
+    const DataTransferSession::CastorConf &conf) {
+
+  const messages::ForkDataTransfer rqst = createForkDataTransferMsg(driveConfig,
+    vdqmJob, conf);
 
   // Request the process forker to fork a data-transfer session
-  messages::ForkDataTransfer rqst;
-  rqst.set_unitname(unitName);
   ProcessForkerUtils::writeFrame(m_socketFd, rqst);
 
   // Read back the reply
@@ -89,12 +91,66 @@ void castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
   m_log(LOG_INFO,
     "Got process ID of the data-transfer session from the ProcessForker",
     params);
+
+  return reply.pid();
+}
+
+//------------------------------------------------------------------------------
+// createForkDataTransferMsg
+//------------------------------------------------------------------------------
+castor::messages::ForkDataTransfer
+  castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
+  createForkDataTransferMsg(const utils::DriveConfig &driveConfig,
+    const legacymsg::RtcpJobRqstMsgBody vdqmJob,
+    const DataTransferSession::CastorConf &config) {
+  messages::ForkDataTransfer msg;
+
+  // Description of the tape drive
+  msg.set_unitname(driveConfig.unitName);
+  msg.set_dgn(driveConfig.dgn);
+  msg.set_devfilename(driveConfig.devFilename);
+  const std::list<std::string> &densities = driveConfig.densities;
+  for(std::list<std::string>::const_iterator itor = densities.begin();
+    itor != densities.end(); itor++) {
+    msg.add_density(*itor);
+  }
+  msg.set_libraryslot(driveConfig.librarySlot);
+  msg.set_devtype(driveConfig.devType);
+
+  // Description of the client request
+  msg.set_mounttransactionid(vdqmJob.volReqId);
+  msg.set_clientport(vdqmJob.clientPort);
+  msg.set_clienteuid(vdqmJob.clientEuid);
+  msg.set_clientegid(vdqmJob.clientEgid);
+  msg.set_clienthost(vdqmJob.clientHost);
+  msg.set_clientusername(vdqmJob.clientUserName);
+
+  // Configuration parameters of the session
+  msg.set_memblocksize(config.rtcopydBufsz);
+  msg.set_nbmemblocks(config.rtcopydNbBufs);
+  msg.set_badmirhandling(config.tapeBadMIRHandlingRepair);
+  msg.set_bulkrequestmigrationmaxbytes(
+    config.tapebridgeBulkRequestMigrationMaxBytes);
+  msg.set_bulkrequestmigrationmaxfiles(
+    config.tapebridgeBulkRequestMigrationMaxFiles);
+  msg.set_bulkrequestrecallmaxbytes(
+    config.tapebridgeBulkRequestRecallMaxBytes);
+  msg.set_bulkrequestrecallmaxfiles(
+    config.tapebridgeBulkRequestRecallMaxFiles);
+  msg.set_maxbytesbeforeflush(
+    config.tapebridgeMaxBytesBeforeFlush);
+  msg.set_maxfilesbeforeflush(
+    config.tapebridgeMaxFilesBeforeFlush);
+  msg.set_diskthreadpoolsize(
+    config.tapeserverdDiskThreads);
+
+  return msg;
 }
 
 //------------------------------------------------------------------------------
 // forkLabel
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
+pid_t castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
   forkLabel(const std::string &unitName, const std::string &vid) {
 
   // Request the process forker to fork a label session
@@ -109,12 +165,14 @@ void castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
   log::Param params[] = {log::Param("pid", reply.pid())};
   m_log(LOG_INFO, "Got process ID of the label session from the ProcessForker",
     params);
+
+  return reply.pid();
 }
 
 //------------------------------------------------------------------------------
 // forkCleaner
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
+pid_t castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
   forkCleaner(const std::string &unitName, const std::string &vid) {
 
   // Request the process forker to fork a label session
@@ -129,4 +187,6 @@ void castor::tape::tapeserver::daemon::ProcessForkerProxySocket::
   log::Param params[] = {log::Param("pid", reply.pid())};
   m_log(LOG_INFO, "Got process ID of the cleaner session from the ProcessForker",
     params);
+
+  return reply.pid();
 }
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.hpp b/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.hpp
index cd71f138543451c8af482bae6a045a60057bca0c..755b66e1eeabd6a58dc1c9a543a2dc41585ade95 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.hpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerProxySocket.hpp
@@ -24,6 +24,7 @@
 #pragma once
 
 #include "castor/log/Logger.hpp"
+#include "castor/messages/ForkDataTransfer.pb.h"
 #include "castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForkerProxy.hpp"
 
@@ -71,19 +72,25 @@ public:
   void stopProcessForker(const std::string &reason);
 
   /**
-   * Forks a data-transfer process for the specified tape drive.
+   * Forks a data-transfer session for the specified tape drive.
    *
-   * @param unitName The unit name of the tape drive.
+   * @param driveConfig The configuration of the tape drive.
+   * @param vdqmJob The job received from the vdqmd daemon.
+   * @param conf The configuration of the data-transfer session.
+   * @return The process identifier of the newly forked session.
    */
-  void forkDataTransfer(const std::string &unitName);
+  pid_t forkDataTransfer(const utils::DriveConfig &driveConfig,
+    const legacymsg::RtcpJobRqstMsgBody vdqmJob, 
+    const DataTransferSession::CastorConf &conf);
 
   /**
    * Forks a label-session process for the specified tape drive.
    *
    * @param unitName The unit name of the tape drive.
    * @param vid The volume identifier of the tape.
+   * @return The process identifier of the newly forked session.
    */
-  void forkLabel(const std::string &unitName, const std::string &vid);
+  pid_t forkLabel(const std::string &unitName, const std::string &vid);
 
   /**
    * Forks a cleaner session for the specified tape drive.
@@ -93,8 +100,9 @@ public:
    * tape in the drive if there is in fact a tape in the drive and its volume
    * identifier is known.  If the volume identifier is not known then this
    * parameter should be set to an empty string.
+   * @return The process identifier of the newly forked session.
    */
-  void forkCleaner(const std::string &unitName, const std::string &vid);
+  pid_t forkCleaner(const std::string &unitName, const std::string &vid);
 
 private:
 
@@ -109,6 +117,19 @@ private:
    */
   const int m_socketFd;
 
+  /**
+   * Creates a ForkDataTransfer message from the specified tape-drive
+   * configuration, VDQM job and data-transfer session configuration.
+   *
+   * @param driveConfig The configuration of the tape drive.
+   * @param vdqmJob The job received from the vdqmd daemon.
+   * @param config The configuration of the data-transfer session.
+   */
+  messages::ForkDataTransfer createForkDataTransferMsg(
+    const utils::DriveConfig &driveConfig,
+    const legacymsg::RtcpJobRqstMsgBody vdqmJob,
+    const DataTransferSession::CastorConf &config);
+
 }; // class ProcessForkerProxySocket
 
 } // namespace daemon
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerTest.cpp b/castor/tape/tapeserver/daemon/ProcessForkerTest.cpp
index 43e9969563a20ab6144d8aa386042e515a245870..377ced2250df29cf017d221bc0b4292e56043d36 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerTest.cpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerTest.cpp
@@ -46,38 +46,52 @@ protected:
 TEST_F(castor_tape_tapeserver_daemon_ProcessForkerTest, constructor) {
   using namespace castor::tape::tapeserver::daemon;
 
-  int sv[2] = {-1, -1};
-  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sv));
-  castor::utils::SmartFd proxySocketfd(sv[0]);
-  castor::utils::SmartFd processForkerSocketfd(sv[1]);
+  int cmdPair[2] = {-1, -1};
+  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, cmdPair));
+  castor::utils::SmartFd cmdSenderSocket(cmdPair[0]);
+  castor::utils::SmartFd cmdReceiverSocket(cmdPair[1]);
+
+  int reaperPair[2] = {-1, -1};
+  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, reaperPair));
+  castor::utils::SmartFd reaperSenderSocket(reaperPair[0]);
+  castor::utils::SmartFd reaperReceiverSocket(reaperPair[1]);
 
   const std::string programName = "unittests";
+  const std::string hostName = "hostName";
   castor::log::DummyLogger log(programName);
   std::auto_ptr<ProcessForker> processForker;
   ASSERT_NO_THROW(processForker.reset(
-    new ProcessForker(log, processForkerSocketfd.get())));
-  processForkerSocketfd.release();
+    new ProcessForker(log, cmdReceiverSocket.get(), reaperSenderSocket.get(),
+      hostName)));
+  cmdReceiverSocket.release();
 }
 
 TEST_F(castor_tape_tapeserver_daemon_ProcessForkerTest, socketproxy) {
   using namespace castor::tape::tapeserver::daemon;
 
-  int sv[2] = {-1, -1};
-  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sv));
-  castor::utils::SmartFd proxySocketfd(sv[0]);
-  castor::utils::SmartFd processForkerSocketfd(sv[1]);
+  int cmdPair[2] = {-1, -1};
+  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, cmdPair));
+  castor::utils::SmartFd cmdSenderSocket(cmdPair[0]);
+  castor::utils::SmartFd cmdReceiverSocket(cmdPair[1]);
+
+  int reaperPair[2] = {-1, -1};
+  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, reaperPair));
+  castor::utils::SmartFd reaperSenderSocket(reaperPair[0]);
+  castor::utils::SmartFd reaperReceiverSocket(reaperPair[1]);
   
   const std::string programName = "unittests";
+  const std::string hostName = "hostName";
   castor::log::DummyLogger log(programName);
   std::auto_ptr<ProcessForker> processForker;
   ASSERT_NO_THROW(processForker.reset(
-    new ProcessForker(log, processForkerSocketfd.get())));
-  processForkerSocketfd.release();
+    new ProcessForker(log, cmdReceiverSocket.get(), reaperSenderSocket.get(),
+      hostName)));
+  cmdReceiverSocket.release();
 
   std::auto_ptr<ProcessForkerProxySocket> processForkerProxy;
   ASSERT_NO_THROW(processForkerProxy.reset(
-    new ProcessForkerProxySocket(log, proxySocketfd.get())));
-  proxySocketfd.release();
+    new ProcessForkerProxySocket(log, cmdSenderSocket.get())));
+  cmdSenderSocket.release();
 } 
 
 } // namespace unitTests
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerUtils.cpp b/castor/tape/tapeserver/daemon/ProcessForkerUtils.cpp
index 2d03b159662b0fc1504625eeb83c154a1aa5283a..28a78e8fd5f00f44d830c849bfee120afff7b8b6 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerUtils.cpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerUtils.cpp
@@ -22,7 +22,6 @@
  *****************************************************************************/
 
 #include "castor/exception/Exception.hpp"
-#include "castor/messages/Status.pb.h"
 #include "castor/tape/tapeserver/daemon/ProcessForkerUtils.hpp"
 #include "castor/utils/SmartArrayPtr.hpp"
 #include "h/serrno.h"
@@ -85,6 +84,34 @@ void castor::tape::tapeserver::daemon::ProcessForkerUtils::serializePayload(
   }   
 }  
 
+//------------------------------------------------------------------------------
+// serializePayload
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerUtils::serializePayload(
+  ProcessForkerFrame &frame, const messages::ProcessCrashed &msg) {
+  frame.type = ProcessForkerMsgType::MSG_PROCESSCRASHED;
+  if(!msg.SerializeToString(&frame.payload)) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to serialize ProcessCrashed payload"
+      ": SerializeToString() returned false";
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// serializePayload
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerUtils::serializePayload(
+  ProcessForkerFrame &frame, const messages::ProcessExited &msg) {
+  frame.type = ProcessForkerMsgType::MSG_PROCESSEXITED;
+  if(!msg.SerializeToString(&frame.payload)) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to serialize ProcessExited payload"
+      ": SerializeToString() returned false";
+    throw ex;
+  }
+}
+
 //------------------------------------------------------------------------------
 // serializePayload
 //------------------------------------------------------------------------------
@@ -103,11 +130,11 @@ void castor::tape::tapeserver::daemon::ProcessForkerUtils::serializePayload(
 // serializePayload
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::ProcessForkerUtils::serializePayload(
-  ProcessForkerFrame &frame, const messages::Status &msg) {
-  frame.type = ProcessForkerMsgType::MSG_STATUS;
+  ProcessForkerFrame &frame, const messages::Exception &msg) {
+  frame.type = ProcessForkerMsgType::MSG_EXCEPTION;
   if(!msg.SerializeToString(&frame.payload)) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to serialize Status payload"
+    ex.getMessage() << "Failed to serialize Exception payload"
       ": SerializeToString() returned false";
     throw ex;
   }
@@ -141,8 +168,24 @@ void castor::tape::tapeserver::daemon::ProcessForkerUtils::writeFrame(
 // writeFrame
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::ProcessForkerUtils::
-  writeFrame(const int fd, const messages::Status &msg) {
-  writeFrame(fd, ProcessForkerMsgType::MSG_STATUS, msg);
+  writeFrame(const int fd, const messages::Exception &msg) {
+  writeFrame(fd, ProcessForkerMsgType::MSG_EXCEPTION, msg);
+}
+
+//------------------------------------------------------------------------------
+// writeFrame
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerUtils::
+  writeFrame(const int fd, const messages::ProcessCrashed &msg) {
+  writeFrame(fd, ProcessForkerMsgType::MSG_PROCESSCRASHED, msg);
+}
+
+//------------------------------------------------------------------------------
+// writeFrame
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerUtils::
+  writeFrame(const int fd, const messages::ProcessExited &msg) {
+  writeFrame(fd, ProcessForkerMsgType::MSG_PROCESSEXITED, msg);
 }
 
 //------------------------------------------------------------------------------
@@ -168,8 +211,22 @@ void castor::tape::tapeserver::daemon::ProcessForkerUtils::
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::ProcessForkerUtils::
   writeFrame(const int fd, const ProcessForkerFrame &frame) {
-  writeFrameHeader(fd, frame.type, frame.payload.length());
-  writeFramePayload(fd, frame.payload);
+  try {
+    if(0 > fd) {
+      castor::exception::Exception ex;
+      ex.getMessage() << "Invalid file-descriptor";
+      throw ex;
+    }
+
+    writeFrameHeader(fd, frame.type, frame.payload.length());
+    writeFramePayload(fd, frame.payload);
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to write ProcessForkerFrame: fd=" << fd <<
+      " type=" << ProcessForkerMsgType::toString(frame.type) << " payloadLen="
+      << frame.payload.length() << ": " << ne.getMessage().str();
+    throw ex;
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -179,6 +236,12 @@ void castor::tape::tapeserver::daemon::ProcessForkerUtils::
   writeFrameHeader(const int fd, const ProcessForkerMsgType::Enum type,
   const uint32_t payloadLen) {
   try {
+    if(0 == payloadLen) {
+      castor::exception::Exception ex;
+      ex.getMessage() << "Payload length must be greater than 0";
+      throw ex;
+    }
+
     writeUint32(fd, type);
     writeUint32(fd, payloadLen);
   } catch(castor::exception::Exception &ne) {
@@ -406,23 +469,44 @@ std::string castor::tape::tapeserver::daemon::ProcessForkerUtils::
 void castor::tape::tapeserver::daemon::ProcessForkerUtils::readAck(
   const int fd) {
   const ProcessForkerFrame frame = readFrame(fd);
-  messages::Status msg;
+  messages::Exception msg;
 
   if(!msg.ParseFromString(frame.payload)) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to parse Status message"
+    ex.getMessage() << "Failed to parse Exception message"
       ": ParseFromString() returned false";
     throw ex;
   }
 
   // Throw an exception if the acknowledge is negative
-  if(0 != msg.status()) {
-    castor::exception::Exception ex(msg.status());
+  if(0 != msg.code()) {
+    castor::exception::Exception ex(msg.code());
     ex.getMessage() << msg.message();
     throw ex;
   }
 }
 
+//------------------------------------------------------------------------------
+// parsePayload
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerUtils::parsePayload(
+  const ProcessForkerFrame &frame, messages::Exception &msg) {
+  if(ProcessForkerMsgType::MSG_EXCEPTION != frame.type) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to parse Exception payload"
+      ": Unexpected message type: type=" <<
+      ProcessForkerMsgType::toString(frame.type);
+    throw ex;
+  }   
+    
+  if(!msg.ParseFromString(frame.payload)) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to parse Exception payload"
+      ": ParseString() returned false: payloadLen="  << frame.payload.length();
+    throw ex;
+  }
+}
+
 //------------------------------------------------------------------------------
 // parsePayload
 //------------------------------------------------------------------------------
@@ -511,23 +595,44 @@ void castor::tape::tapeserver::daemon::ProcessForkerUtils::parsePayload(
 // parsePayload
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::ProcessForkerUtils::parsePayload(
-  const ProcessForkerFrame &frame, messages::Status &msg) {
-  if(ProcessForkerMsgType::MSG_STATUS != frame.type) {
+  const ProcessForkerFrame &frame, messages::ProcessCrashed &msg) {
+  if(ProcessForkerMsgType::MSG_PROCESSCRASHED != frame.type) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to parse Status payload"
+    ex.getMessage() << "Failed to parse ProcessCrashed payload"
       ": Unexpected message type: type=" <<
       ProcessForkerMsgType::toString(frame.type);
     throw ex;
-  }   
-    
+  }
+
   if(!msg.ParseFromString(frame.payload)) {
     castor::exception::Exception ex;
-    ex.getMessage() << "Failed to parse Status payload"
+    ex.getMessage() << "Failed to parse ProcessCrashed payload"
       ": ParseString() returned false: payloadLen="  << frame.payload.length();
     throw ex;
   }
 }
 
+//------------------------------------------------------------------------------
+// parsePayload
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::ProcessForkerUtils::parsePayload(
+  const ProcessForkerFrame &frame, messages::ProcessExited &msg) {
+  if(ProcessForkerMsgType::MSG_PROCESSEXITED != frame.type) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to parse ProcessExited payload"
+      ": Unexpected message type: type=" <<
+      ProcessForkerMsgType::toString(frame.type);
+    throw ex;
+  }   
+    
+  if(!msg.ParseFromString(frame.payload)) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to parse ProcessExited payload"
+      ": ParseString() returned false: payloadLen="  << frame.payload.length();
+    throw ex;
+  }   
+}   
+
 //------------------------------------------------------------------------------
 // parsePayload
 //------------------------------------------------------------------------------
diff --git a/castor/tape/tapeserver/daemon/ProcessForkerUtils.hpp b/castor/tape/tapeserver/daemon/ProcessForkerUtils.hpp
index 7ef740fa645d05b4324db6ff50c9700606d6b640..8a1e4498f4b71fccb2f44a9447b3fdd04d3e8b15 100644
--- a/castor/tape/tapeserver/daemon/ProcessForkerUtils.hpp
+++ b/castor/tape/tapeserver/daemon/ProcessForkerUtils.hpp
@@ -23,11 +23,13 @@
 
 #pragma once
 
+#include "castor/messages/Exception.pb.h"
 #include "castor/messages/ForkCleaner.pb.h"
 #include "castor/messages/ForkDataTransfer.pb.h"
 #include "castor/messages/ForkLabel.pb.h"
 #include "castor/messages/ForkSucceeded.pb.h"
-#include "castor/messages/Status.pb.h"
+#include "castor/messages/ProcessCrashed.pb.h"
+#include "castor/messages/ProcessExited.pb.h"
 #include "castor/messages/StopProcessForker.pb.h"
 #include "castor/tape/tapeserver/daemon/ProcessForkerFrame.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForkerMsgType.hpp"
@@ -48,6 +50,18 @@ namespace daemon     {
 class ProcessForkerUtils {
 public:
 
+  /**
+   * Serializes the specified message into the specified frame.
+   *
+   * Please note that this method sets both the type and payload fields of the
+   * frame.
+   *
+   * @param frame Output parameter: The frame.
+   * @param msg The message.
+   */
+  static void serializePayload(ProcessForkerFrame &frame,
+    const messages::Exception &msg);
+
   /**
    * Serializes the specified message into the specified frame.
    *
@@ -97,8 +111,20 @@ public:
     const messages::ForkSucceeded &msg);
 
   /**
-   * Serializes the specified message into the specified frame.
+   * Serializes the specified message into the specified frame. 
+   *  
+   * Please note that this method sets both the type and payload fields of the
+   * frame.
    *
+   * @param frame Output parameter: The frame.
+   * @param msg The message.
+   */
+  static void serializePayload(ProcessForkerFrame &frame,
+    const messages::ProcessCrashed &msg);
+
+  /**
+   * Serializes the specified message into the specified frame. 
+   *  
    * Please note that this method sets both the type and payload fields of the
    * frame.
    *
@@ -106,7 +132,7 @@ public:
    * @param msg The message.
    */
   static void serializePayload(ProcessForkerFrame &frame,
-    const messages::Status &msg);
+    const messages::ProcessExited &msg);
 
   /**
    * Serializes the specified message into the specified frame.
@@ -120,6 +146,15 @@ public:
   static void serializePayload(ProcessForkerFrame &frame,
     const messages::StopProcessForker &msg);
 
+  /**
+   * Writes a frame with the specified message as its payload to the specified
+   * file descriptor.
+   *
+   * @param fd The file descriptor to be written to.
+   * @param msg The message to sent as the payload of the frame.
+   */
+  static void writeFrame(const int fd, const messages::Exception &msg);
+
   /**
    * Writes a frame with the specified message as its payload to the specified
    * file descriptor.
@@ -154,7 +189,16 @@ public:
    * @param fd The file descriptor to be written to.
    * @param msg The message to sent as the payload of the frame.
    */
-  static void writeFrame(const int fd, const messages::Status &msg);
+  static void writeFrame(const int fd, const messages::ProcessCrashed &msg);
+
+  /**
+   * Writes a frame with the specified message as its payload to the specified
+   * file descriptor.
+   *
+   * @param fd The file descriptor to be written to.
+   * @param msg The message to sent as the payload of the frame.
+   */
+  static void writeFrame(const int fd, const messages::ProcessExited &msg);
 
   /**
    * Writes a frame with the specified message as its payload to the specified
@@ -184,12 +228,12 @@ public:
    * Reads a good-day reply or an exception from the specified file descriptor.
    *
    * This method deals with two types of message, the one the caller wishes to
-   * read in the good-day scenario and the messages::Status message in the
-   * bad-day scenario where the ProcessForker replies with an error/exception.
+   * read in the good-day scenario and the messages::Exception message in the
+   * bad-day scenario where the ProcessForker replies with an exception.
    *
-   * If the ProcessForker replies with an error/exception in the form of a
-   * messages::Status message, then this method will convert the message into
-   * an exception.
+   * If the ProcessForker replies with an exception in the form of a
+   * messages::Exception message, then this method will convert the message into
+   * a C++ exception.
    *
    * @param fd The file descriptor to be read from.
    * @param msgType The type of the message.
@@ -198,17 +242,17 @@ public:
   template<typename T> static void readReplyOrEx(const int fd, T &msg) {
     const ProcessForkerFrame frame = readFrame(fd);
 
-    // Throw an exception if the ProcessForker replies with an error
-    if(ProcessForkerMsgType::MSG_STATUS == frame.type) {
-      messages::Status errMsg;
-      if(!errMsg.ParseFromString(frame.payload)) {
+    // Throw an exception if the ProcessForker replied with one
+    if(ProcessForkerMsgType::MSG_EXCEPTION == frame.type) {
+      messages::Exception exMsg;
+      if(!exMsg.ParseFromString(frame.payload)) {
         castor::exception::Exception ex;
-        ex.getMessage() << "Failed to parse Status message"
+        ex.getMessage() << "Failed to parse Exception message"
           ": ParseFromString() returned false";
         throw ex;
       }
-      castor::exception::Exception ex(errMsg.status());
-      ex.getMessage() << errMsg.message();
+      castor::exception::Exception ex(exMsg.code());
+      ex.getMessage() << exMsg.message();
       throw ex;
     }
 
@@ -223,6 +267,16 @@ public:
    */
   static ProcessForkerFrame readFrame(const int fd);
 
+  /**
+   * Parses the payload of the specified frame.
+   *
+   * @param frame The frame.
+   * @param msg Output parameter: The message contained within the payload of
+   * the frame.
+   */
+  static void parsePayload(const ProcessForkerFrame &frame,
+    messages::Exception &msg);
+
   /**
    * Parses the payload of the specified frame.
    *
@@ -271,7 +325,17 @@ public:
    * the frame.
    */
   static void parsePayload(const ProcessForkerFrame &frame,
-    messages::Status &msg);
+    messages::ProcessCrashed &msg);
+
+  /**
+   * Parses the payload of the specified frame.
+   *
+   * @param frame The frame.
+   * @param msg Output parameter: The message contained within the payload of
+   * the frame.
+   */
+  static void parsePayload(const ProcessForkerFrame &frame,
+    messages::ProcessExited &msg);
 
   /**
    * Parses the payload of the specified frame.
diff --git a/castor/tape/tapeserver/daemon/TapeDaemon.cpp b/castor/tape/tapeserver/daemon/TapeDaemon.cpp
index 8c3b91a5701d0597e18f605ecb0d3112a4c58a23..853e5d30de6e3754f5fb1c0bedc797a96c45a3b7 100644
--- a/castor/tape/tapeserver/daemon/TapeDaemon.cpp
+++ b/castor/tape/tapeserver/daemon/TapeDaemon.cpp
@@ -34,6 +34,7 @@
 #include "castor/tape/tapeserver/daemon/LabelCmdAcceptHandler.hpp"
 #include "castor/tape/tapeserver/daemon/LabelSession.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForker.hpp"
+#include "castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.hpp"
 #include "castor/tape/tapeserver/daemon/ProcessForkerProxySocket.hpp"
 #include "castor/tape/tapeserver/daemon/TapeDaemon.hpp"
 #include "castor/tape/tapeserver/daemon/TapeMessageHandler.hpp"
@@ -265,36 +266,21 @@ void castor::tape::tapeserver::daemon::TapeDaemon::setProcessCapabilities(
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::TapeDaemon::forkProcessForker() {
   m_log.prepareForFork();
-  // Create a socket pair for controlling the ProcessForker
-  int sv[2] = {-1 , -1};
-  if(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
-    char message[100];
-    strerror_r(errno, message, sizeof(message));
-    castor::exception::Exception ex;
-    ex.getMessage() << "Failed to fork process forker: Failed to create socket"
-      " pair for controlling the ProcessForker: " << message;
-    throw ex;
-  }
 
-  const int processForkerCmdSenderSocket = sv[0];
-  const int processForkerCmdReceiverSocket = sv[1];
-
-  {
-    log::Param params[] = {
-      log::Param("cmdSenderSocket", processForkerCmdSenderSocket),
-      log::Param("cmdReceiverSocket", processForkerCmdReceiverSocket)};
-    m_log(LOG_INFO, "TapeDaemon parent process succesfully created socket"
-      " pair for controlling the ProcessForker", params);
-  }
+  // Create two socket pairs for ProcessForker communications
+  const ForkerCmdPair cmdPair = createForkerCmdPair();
+  const ForkerReaperPair reaperPair = createForkerReaperPair();
 
   const pid_t forkRc = fork();
 
   // If fork failed
   if(0 > forkRc) {
-    close(processForkerCmdReceiverSocket);
-
     char message[100];
     sstrerror_r(errno, message, sizeof(message));
+
+    closeForkerCmdPair(cmdPair);
+    closeForkerReaperPair(reaperPair);
+
     castor::exception::Exception ex;
     ex.getMessage() << "Failed to fork ProcessForker: " << message;
     throw ex;
@@ -309,24 +295,16 @@ void castor::tape::tapeserver::daemon::TapeDaemon::forkProcessForker() {
       m_log(LOG_INFO, "Successfully forked the ProcessForker", params);
     }
 
-    if(close(processForkerCmdReceiverSocket)) {
-      char message[100];
-      sstrerror_r(errno, message, sizeof(message));
-      castor::exception::Exception ex;
-      ex.getMessage() << "TapeDaemon parent process failed to close the socket"
-        " used to receive ProcessForker commands: " << message;
-      throw ex;
-    }
+    closeProcessForkerSideOfCmdPair(cmdPair);
+    closeProcessForkerSideOfReaperPair(reaperPair);
 
-    {
-      log::Param params[] =
-        {log::Param("cmdReceiverSocket", processForkerCmdReceiverSocket)};
-      m_log(LOG_INFO, "TapeDaemon parent process successfully closed the socket"
-        " used to receive ProcessForker commands", params);
-    }
+    m_processForker = new ProcessForkerProxySocket(m_log, cmdPair.tapeDaemon);
+    log::Param params[] =
+      {log::Param("cmdPair.tapeDaemon", cmdPair.tapeDaemon)};
+    m_log(LOG_INFO, "TapeDaemon parent process created ProcessForker proxy",
+      params);
 
-    m_processForker =
-      new ProcessForkerProxySocket(m_log, processForkerCmdSenderSocket);
+    createAndRegisterProcessForkerConnectionHandler(reaperPair.tapeDaemon);
 
     return;
 
@@ -336,48 +314,260 @@ void castor::tape::tapeserver::daemon::TapeDaemon::forkProcessForker() {
     // file-descriptors owned by the event handlers
     m_reactor.clear();
 
-    if(close(processForkerCmdSenderSocket)) {
-      char message[100];
-      sstrerror_r(errno, message, sizeof(message));
-      castor::exception::Exception ex;
-      ex.getMessage() << "ProcessForker process failed to close the socket"
-        " used to send ProcessForker commands: " << message;
-      throw ex;
-    }
+    closeTapeDaemonSideOfCmdPair(cmdPair);
+    closeTapeDaemonSideOfReaperPair(reaperPair);
 
-    {
-      log::Param params[] = 
-        {log::Param("cmdSenderSocket", processForkerCmdSenderSocket)};
-      m_log(LOG_INFO, "ProcessForker process successfully closed the socket" 
-        " used to send ProcessForker commands", params);
-    }
+    exit(runProcessForker(cmdPair.processForker, reaperPair.processForker));
+  }
+}
+
+//------------------------------------------------------------------------------
+// createForkerCmdPair
+//------------------------------------------------------------------------------
+castor::tape::tapeserver::daemon::TapeDaemon::ForkerCmdPair
+  castor::tape::tapeserver::daemon::TapeDaemon::createForkerCmdPair() {
+  ForkerCmdPair cmdPair;
+
+  try {
+    const std::pair<int, int> socketPair = createSocketPair();
+    cmdPair.tapeDaemon = socketPair.first;
+    cmdPair.processForker = socketPair.second;
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to create socket pair to control the"
+      " ProcessForker: " << ne.getMessage().str();
+    throw ex; 
+  }
+
+  {
+    log::Param params[] = {
+      log::Param("cmdPair.tapeDaemon", cmdPair.tapeDaemon),
+      log::Param("cmdPair.processForker", cmdPair.processForker)};
+    m_log(LOG_INFO, "TapeDaemon parent process succesfully created socket"
+      " pair to control the ProcessForker", params);
+  }
+
+  return cmdPair;
+}
+
+//------------------------------------------------------------------------------
+// createForkerReaperPair
+//------------------------------------------------------------------------------
+castor::tape::tapeserver::daemon::TapeDaemon::ForkerReaperPair
+  castor::tape::tapeserver::daemon::TapeDaemon::createForkerReaperPair() {
+  ForkerReaperPair reaperPair;
+
+  try {
+    const std::pair<int, int> socketPair = createSocketPair();
+    reaperPair.tapeDaemon = socketPair.first;
+    reaperPair.processForker = socketPair.second;
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to create socket pair for the ProcessForker"
+      " to report terminated processes: " << ne.getMessage().str();
+    throw ex;
+  }
+
+  {
+    log::Param params[] = {
+      log::Param("reaperPair.tapeDaemon", reaperPair.tapeDaemon),
+      log::Param("reaperPair.processForker", reaperPair.processForker)};
+    m_log(LOG_INFO, "TapeDaemon parent process succesfully created socket"
+      " pair for ProcessForker to report terminated processes", params);
+  }
+
+  return reaperPair;
+}
+
+//------------------------------------------------------------------------------
+// createSocketPair
+//------------------------------------------------------------------------------
+std::pair<int, int>
+  castor::tape::tapeserver::daemon::TapeDaemon::createSocketPair() {
+  int sv[2] = {-1, -1};
+  if(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
+    char message[100];
+    strerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to create socket pair: " << message;
+    throw ex;
+  }
+
+  return std::pair<int, int> (sv[0], sv[1]);
+}
+
+//------------------------------------------------------------------------------
+// closeForkerCmdPair
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeDaemon::closeForkerCmdPair(
+  const ForkerCmdPair &cmdPair) {
+  if(close(cmdPair.tapeDaemon)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to close TapeDaemon side of cmdPair"
+      ": cmdPair.tapeDaemon=" << cmdPair.tapeDaemon << ": " << message;
+    throw ex;
+  } else {
+    log::Param params[] =
+      {log::Param("cmdPair.tapeDaemon", cmdPair.tapeDaemon)};
+    m_log(LOG_INFO, "Successfully closed TapeDaemon side of cmdPair",
+      params);
+  }
+
+  if(close(cmdPair.processForker)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to close ProcessForker side of cmdPair"
+      ": cmdPair.processForker=" << cmdPair.processForker << ": " << message;
+    throw ex;
+  } else {
+    log::Param params[] =
+      {log::Param("cmdPair.processForker", cmdPair.processForker)};
+    m_log(LOG_INFO, "Successfully closed ProcessForker side of cmdPair",
+      params);
+  }
+}
+
+//------------------------------------------------------------------------------
+// closeForkerReaperPair
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeDaemon::closeForkerReaperPair(
+  const ForkerReaperPair &reaperPair) {
+  if(close(reaperPair.tapeDaemon)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to close TapeDaemon side of reaperPair"
+      ": reaperPair.tapeDaemon=" << reaperPair.tapeDaemon << ": " << message;
+    throw ex;
+  } else {
+    log::Param params[] =
+      {log::Param("reaperPair.tapeDaemon", reaperPair.tapeDaemon)};
+    m_log(LOG_INFO, "Successfully closed TapeDaemon side of reaperPair",
+      params);
+  }
+
+  if(close(reaperPair.processForker)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to close ProcessForker side of reaperPair"
+      ": reaperPair.processForker=" << reaperPair.processForker << ": " <<
+      message;
+    throw ex;
+  } else {
+    log::Param params[] =
+      {log::Param("reaperPair.processForker", reaperPair.processForker)};
+    m_log(LOG_INFO, "Successfully closed ProcessForker side of reaperPair",
+      params);
+  }
+}
 
-    exit(runProcessForker(processForkerCmdReceiverSocket));
+//------------------------------------------------------------------------------
+// closeProcessForkerSideOfCmdPair
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeDaemon::
+  closeProcessForkerSideOfCmdPair(const ForkerCmdPair &cmdPair) {
+  if(close(cmdPair.processForker)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "TapeDaemon parent process failed to close"
+      " ProcessForker side of cmdPair: cmdPair.processForker=" <<
+      cmdPair.processForker << ": " << message;
+    throw ex;
   }
+
+  log::Param params[] =
+    {log::Param("cmdPair.processForker", cmdPair.processForker)};
+    m_log(LOG_INFO, "TapeDaemon parent process successfully closed"
+      " ProcessForker side of cmdPair", params);
+}
+
+//------------------------------------------------------------------------------
+// closeProcessForkerSideOfReaperPair
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeDaemon::
+  closeProcessForkerSideOfReaperPair(const ForkerReaperPair &reaperPair) {
+  if(close(reaperPair.processForker)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "TapeDaemon parent process failed to close"
+      " ProcessForker side of reaperPair: reaperPair.processForker=" <<
+      reaperPair.processForker << ": " << message;
+    throw ex;
+  }
+
+  log::Param params[] =
+    {log::Param("reaperPair.processForker)", reaperPair.processForker)};
+  m_log(LOG_INFO, "TapeDaemon parent process successfully closed"
+    " ProcessForker side of reaperPair", params);
+}
+
+//------------------------------------------------------------------------------
+// closeTapeDaemonSideOfCmdPair
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeDaemon::
+  closeTapeDaemonSideOfCmdPair(const ForkerCmdPair &cmdPair) {
+  if(close(cmdPair.tapeDaemon)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "ProcessForker process failed to close"
+      " TapeDaemon side of cmdPair: cmdPair.tapeDaemon=" << cmdPair.tapeDaemon
+      << ": " << message;
+    throw ex;
+  }
+
+  log::Param params[] = {log::Param("cmdPair.tapeDaemon", cmdPair.tapeDaemon)};
+    m_log(LOG_INFO, "ProcessForker process successfully closed"
+      " TapeDaemon side of cmdPair", params);
+}
+
+//------------------------------------------------------------------------------
+// closeTapeDaemonSideOfReaperPair
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeDaemon::
+  closeTapeDaemonSideOfReaperPair(const ForkerReaperPair &reaperPair) {
+  if(close(reaperPair.tapeDaemon)) {
+    char message[100];
+    sstrerror_r(errno, message, sizeof(message));
+    castor::exception::Exception ex;
+    ex.getMessage() << "ProcessForker process failed to close"
+      " TapeDaemon side of reaperPair: reaperPair.tapeDaemon=" <<
+      reaperPair.tapeDaemon << ": " << message;
+    throw ex;
+  }
+
+  log::Param params[] =
+    {log::Param("reaperPair.tapeDaemon", reaperPair.tapeDaemon)};
+  m_log(LOG_INFO, "ProcessForker parent process successfully closed"
+    " TapeDaemon side of reaperPair", params);
 }
 
 //------------------------------------------------------------------------------
 // runProcessForker
 //------------------------------------------------------------------------------
 int castor::tape::tapeserver::daemon::TapeDaemon::runProcessForker(
-  const int cmdReceiverSocket) throw() {
+  const int cmdReceiverSocket, const int reaperSenderSocket) throw() {
   try {
-    ProcessForker processForker(m_log, cmdReceiverSocket);
+    ProcessForker processForker(m_log, cmdReceiverSocket, reaperSenderSocket,
+      m_hostName);
     processForker.execute();
+    return 0;
   } catch(castor::exception::Exception &ex) {
     log::Param params[] = {log::Param("message", ex.getMessage().str())};
     m_log(LOG_ERR, "ProcessForker threw an unexpected exception", params);
-    return 1;
   } catch(std::exception &se) {
     log::Param params[] = {log::Param("message", se.what())};
     m_log(LOG_ERR, "ProcessForker threw an unexpected exception", params);
-    return 1;
   } catch(...) {
     m_log(LOG_ERR, "ProcessForker threw an unknown and unexpected exception");
-    return 1;
   }
-
-  return 0;
+  return 1;
 }
 
 //------------------------------------------------------------------------------
@@ -483,7 +673,8 @@ void castor::tape::tapeserver::daemon::TapeDaemon::setUpReactor() {
 //------------------------------------------------------------------------------
 // createAndRegisterVdqmAcceptHandler
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterVdqmAcceptHandler()  {
+void castor::tape::tapeserver::daemon::TapeDaemon::
+  createAndRegisterVdqmAcceptHandler()  {
   castor::utils::SmartFd listenSock;
   try {
     listenSock.reset(io::createListenerSock(TAPE_SERVER_VDQM_LISTENING_PORT));
@@ -499,10 +690,10 @@ void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterVdqmAcceptHa
     m_log(LOG_INFO, "Listening for connections from the vdqmd daemon", params);
   }
 
-  std::auto_ptr<VdqmAcceptHandler> vdqmAcceptHandler;
+  std::auto_ptr<VdqmAcceptHandler> handler;
   try {
-    vdqmAcceptHandler.reset(new VdqmAcceptHandler(listenSock.get(), m_reactor,
-      m_log, m_driveCatalogue));
+    handler.reset(new VdqmAcceptHandler(listenSock.get(), m_reactor, m_log,
+      m_driveCatalogue));
     listenSock.release();
   } catch(std::bad_alloc &ba) {
     castor::exception::BadAlloc ex;
@@ -511,14 +702,15 @@ void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterVdqmAcceptHa
       ": " << ba.what();
     throw ex;
   }
-  m_reactor.registerHandler(vdqmAcceptHandler.get());
-  vdqmAcceptHandler.release();
+  m_reactor.registerHandler(handler.get());
+  handler.release();
 }
 
 //------------------------------------------------------------------------------
 // createAndRegisterAdminAcceptHandler
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterAdminAcceptHandler()  {
+void castor::tape::tapeserver::daemon::TapeDaemon::
+  createAndRegisterAdminAcceptHandler()  {
   castor::utils::SmartFd listenSock;
   try {
     listenSock.reset(io::createListenerSock(TAPE_SERVER_ADMIN_LISTENING_PORT));
@@ -536,10 +728,10 @@ void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterAdminAcceptH
       params);
   }
 
-  std::auto_ptr<AdminAcceptHandler> adminAcceptHandler;
+  std::auto_ptr<AdminAcceptHandler> handler;
   try {
-    adminAcceptHandler.reset(new AdminAcceptHandler(listenSock.get(), m_reactor,
-      m_log, m_vdqm, m_driveCatalogue, m_hostName));
+    handler.reset(new AdminAcceptHandler(listenSock.get(), m_reactor, m_log,
+      m_vdqm, m_driveCatalogue, m_hostName));
     listenSock.release();
   } catch(std::bad_alloc &ba) {
     castor::exception::BadAlloc ex;
@@ -548,8 +740,8 @@ void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterAdminAcceptH
       ": " << ba.what();
     throw ex;
   }
-  m_reactor.registerHandler(adminAcceptHandler.get());
-  adminAcceptHandler.release();
+  m_reactor.registerHandler(handler.get());
+  handler.release();
 }
 
 //------------------------------------------------------------------------------
@@ -573,10 +765,10 @@ void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterLabelCmdAcce
       params);
   }
 
-  std::auto_ptr<LabelCmdAcceptHandler> labelCmdAcceptHandler;
+  std::auto_ptr<LabelCmdAcceptHandler> handler;
   try {
-    labelCmdAcceptHandler.reset(new LabelCmdAcceptHandler(listenSock.get(),
-      m_reactor, m_log, m_driveCatalogue, m_hostName, m_vdqm, m_vmgr));
+    handler.reset(new LabelCmdAcceptHandler(listenSock.get(), m_reactor, m_log,
+      m_driveCatalogue, m_hostName, m_vdqm, m_vmgr));
     listenSock.release();
   } catch(std::bad_alloc &ba) {
     castor::exception::BadAlloc ex;
@@ -585,8 +777,8 @@ void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterLabelCmdAcce
       ": " << ba.what();
     throw ex;
   }
-  m_reactor.registerHandler(labelCmdAcceptHandler.get());
-  labelCmdAcceptHandler.release();
+  m_reactor.registerHandler(handler.get());
+  handler.release();
 }
 
 //------------------------------------------------------------------------------
@@ -594,10 +786,10 @@ void castor::tape::tapeserver::daemon::TapeDaemon::createAndRegisterLabelCmdAcce
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::TapeDaemon::
   createAndRegisterTapeMessageHandler()  {
-  std::auto_ptr<TapeMessageHandler> tapeMessageHandler;
+  std::auto_ptr<TapeMessageHandler> handler;
   try {
-    tapeMessageHandler.reset(new TapeMessageHandler(m_reactor, m_log,
-      m_driveCatalogue, m_hostName, m_vdqm, m_vmgr, m_zmqContext));
+    handler.reset(new TapeMessageHandler(m_reactor, m_log, m_driveCatalogue,
+      m_hostName, m_vdqm, m_vmgr, m_zmqContext));
   } catch(std::bad_alloc &ba) {
     castor::exception::BadAlloc ex;
     ex.getMessage() <<
@@ -605,8 +797,28 @@ void castor::tape::tapeserver::daemon::TapeDaemon::
       ": " << ba.what();
     throw ex;
   }
-  m_reactor.registerHandler(tapeMessageHandler.get());
-  tapeMessageHandler.release();
+  m_reactor.registerHandler(handler.get());
+  handler.release();
+}
+
+//------------------------------------------------------------------------------
+// createAndRegisterProcessForkerConnectionHandler
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeDaemon::
+  createAndRegisterProcessForkerConnectionHandler(const int reaperSocket)  {
+  std::auto_ptr<ProcessForkerConnectionHandler> handler;
+  try {
+    handler.reset(new ProcessForkerConnectionHandler(reaperSocket, m_reactor,
+      m_log, m_driveCatalogue, m_hostName, m_vdqm));
+  } catch(std::bad_alloc &ba) {
+    castor::exception::BadAlloc ex;
+    ex.getMessage() <<
+      "Failed to create event handler for communicating with the ProcessForker"
+      ": " << ba.what();
+    throw ex;
+  }
+  m_reactor.registerHandler(handler.get());
+  handler.release();
 }
 
 //------------------------------------------------------------------------------
@@ -640,8 +852,8 @@ bool castor::tape::tapeserver::daemon::TapeDaemon::handleEvents()
       log::Param("message", ex.getMessage().str()),
       log::Param("backtrace", ex.backtrace())
     };
-    m_log(LOG_ERR, "Unexpected castor exception thrown when handling an I/O event",
-      params);
+    m_log(LOG_ERR,
+      "Unexpected castor exception thrown when handling an I/O event", params);
   } catch(std::exception &se) {
     // Log exception and continue
     log::Param params[] = {log::Param("message", se.what())};
@@ -792,8 +1004,6 @@ void castor::tape::tapeserver::daemon::TapeDaemon::
   const int waitpidStat) {
 
   switch(sessionType) {
-  case DriveCatalogueEntry::SESSION_TYPE_DATATRANSFER:
-    return handleReapedDataTransferSession(pid, waitpidStat);
   case DriveCatalogueEntry::SESSION_TYPE_LABEL:
     return handleReapedLabelSession(pid, waitpidStat);
   case DriveCatalogueEntry::SESSION_TYPE_CLEANER:
@@ -808,37 +1018,6 @@ void castor::tape::tapeserver::daemon::TapeDaemon::
   }
 }
 
-//------------------------------------------------------------------------------
-// handleReapedDataTransferSession
-//------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeDaemon::handleReapedDataTransferSession(
-  const pid_t pid, const int waitpidStat) {
-  try {
-    std::list<log::Param> params;
-    params.push_back(log::Param("dataTransferPid", pid));
-    DriveCatalogueEntry *const drive = m_driveCatalogue.findDrive(pid);
-    const utils::DriveConfig &driveConfig = drive->getConfig();
-
-    if(WIFEXITED(waitpidStat) && 0 == WEXITSTATUS(waitpidStat)) {
-      const std::string vid = drive->getVid();
-      drive->sessionSucceeded();
-      m_log(LOG_INFO, "Data-transfer session succeeded", params);
-      requestVdqmToReleaseDrive(driveConfig, pid);
-      notifyVdqmTapeUnmounted(driveConfig, vid, pid);
-    } else {
-      drive->sessionFailed(); //deletes the session
-      m_log(LOG_INFO, "Data-transfer session failed. Going to try to clean the drive.", params);
-      requestVdqmToReleaseDrive(driveConfig, pid);
-      drive->toBeCleaned();
-    }
-  } catch(castor::exception::Exception &ne) {
-    castor::exception::Exception ex;
-    ex.getMessage() << "Failed to handle reaped data transfer session: " << 
-    ne.getMessage().str();
-    throw ex;
-  }
-}
-
 //------------------------------------------------------------------------------
 // handleReapedCleanerSession
 //------------------------------------------------------------------------------
@@ -987,166 +1166,84 @@ void castor::tape::tapeserver::daemon::TapeDaemon::forkDataTransferSessions()
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::TapeDaemon::forkDataTransferSession(
   DriveCatalogueEntry *drive) throw() {
-  const utils::DriveConfig &driveConfig = drive->getConfig();
-
-  std::list<log::Param> params;
-  params.push_back(log::Param("unitName", driveConfig.unitName));
-
-  m_processForker->forkDataTransfer(driveConfig.unitName);
-
-  m_log.prepareForFork();
-
-  const pid_t forkRc = fork();
-
-  // If fork failed
-  if(0 > forkRc) {
-    // Log an error message and return
-    char message[100];
-    sstrerror_r(errno, message, sizeof(message));
-    params.push_back(log::Param("message", message));
-    m_log(LOG_ERR, "Failed to fork data-transfer session for tape drive",
-      params);
-    return;
+  try {
+    const utils::DriveConfig &driveConfig = drive->getConfig();
+    const legacymsg::RtcpJobRqstMsgBody &vdqmJob = drive->getVdqmJob();
+    const DataTransferSession::CastorConf dataTransferConfig =
+      getDataTransferConf();
 
-  // Else if this is the parent process
-  } else if(0 < forkRc) {
-    drive->forkedDataTransferSession(forkRc);
-    return;
-
-  // Else this is the child process
-  } else {
-    // Clear the reactor which in turn will close all of the open
-    // file-descriptors owned by the event handlers
-    m_reactor.clear();
+    const pid_t dataTransferPid = m_processForker->forkDataTransfer(driveConfig,
+      vdqmJob, dataTransferConfig);
+    drive->forkedDataTransferSession(dataTransferPid);
 
     try {
       m_vdqm.assignDrive(m_hostName, driveConfig.unitName,
-        drive->getVdqmJob().dgn, drive->getVdqmJob().volReqId, getpid());
-      m_log(LOG_INFO, "Assigned the drive in the vdqm");
+        drive->getVdqmJob().dgn, drive->getVdqmJob().volReqId, dataTransferPid);
+      log::Param params[] = {
+        log::Param("server", m_hostName),
+        log::Param("unitName", driveConfig.unitName),
+        log::Param("dgn", std::string(drive->getVdqmJob().dgn)),
+        log::Param("volReqId", drive->getVdqmJob().volReqId),
+        log::Param("dataTransferPid", dataTransferPid)};
+      m_log(LOG_INFO, "Assigned the drive in the vdqm", params);
     } catch(castor::exception::Exception &ex) {
       log::Param params[] = {log::Param("message", ex.getMessage().str())};
       m_log(LOG_ERR, "Data-transfer session could not be started"
         ": Failed to assign drive in vdqm", params);
     }
-
-    runDataTransferSession(drive);
+  } catch(castor::exception::Exception &ex) {
+    log::Param params[] = {log::Param("message", ex.getMessage().str())};
+    m_log(LOG_ERR, "Caught an exception when requesting ProcessForker to fork a"
+      " data-transfer session", params);
+    drive->sessionFailed();
+  } catch(std::exception &se) {
+    log::Param params[] = {log::Param("message", se.what())};
+    m_log(LOG_ERR, "Caught an exception when requesting ProcessForker to fork a"
+      " data-transfer session", params);
+    drive->sessionFailed();
+  } catch(...) {
+    m_log(LOG_ERR, "Caught an unknown exception when requesting ProcessForker"
+      " to fork a data-transfer session");
+    drive->sessionFailed();
   }
 }
 
 //------------------------------------------------------------------------------
-// runDataTransferSession
-//------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeDaemon::runDataTransferSession(
-  const DriveCatalogueEntry *drive) throw() {
-  const utils::DriveConfig &driveConfig = drive->getConfig();
-  std::list<log::Param> params;
-  params.push_back(log::Param("unitName", driveConfig.unitName));
-
-  m_log(LOG_INFO, "Data-transfer child-process started", params);
-  
-  try {
-    DataTransferSession::CastorConf castorConf;
-    // This try bloc will allow us to send a failure notification to the client
-    // if we fail before the DataTransferSession has an opportunity to do so.
-    std::auto_ptr<DataTransferSession> dataTransferSession;
-    castor::tape::System::realWrapper sysWrapper;
-    std::auto_ptr<legacymsg::RmcProxy> rmc;
-    std::auto_ptr<messages::TapeserverProxy> tapeserver;
-    try {
-      common::CastorConfiguration &config =
-        common::CastorConfiguration::getConfig();
-      castorConf.rtcopydBufsz = config.getConfEntInt(
-        "RTCOPYD", "BUFSZ", (uint32_t)RTCP_BUFSZ, &m_log);
-      castorConf.rtcopydNbBufs = config.getConfEntInt(
-        "RTCOPYD", "NB_BUFS", (uint32_t)NB_RTCP_BUFS, &m_log);
-      castorConf.tapeBadMIRHandlingRepair = config.getConfEntString(
-        "TAPE", "BADMIR_HANDLING", "CANCEL", &m_log);
-      castorConf.tapebridgeBulkRequestMigrationMaxBytes = config.getConfEntInt(
-        "TAPEBRIDGE", "BULKREQUESTMIGRATIONMAXBYTES",
-        (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTMIGRATIONMAXBYTES, &m_log);
-      castorConf.tapebridgeBulkRequestMigrationMaxFiles = config.getConfEntInt(
-        "TAPEBRIDGE", "BULKREQUESTMIGRATIONMAXFILES",
-        (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTMIGRATIONMAXFILES, &m_log);
-      castorConf.tapebridgeBulkRequestRecallMaxBytes = config.getConfEntInt(
-        "TAPEBRIDGE", "BULKREQUESTRECALLMAXBYTES",
-        (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTRECALLMAXBYTES, &m_log);
-      castorConf.tapebridgeBulkRequestRecallMaxFiles = config.getConfEntInt(
-        "TAPEBRIDGE", "BULKREQUESTRECALLMAXFILES",
-        (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTRECALLMAXFILES, &m_log);
-      castorConf.tapebridgeMaxBytesBeforeFlush = config.getConfEntInt(
-        "TAPEBRIDGE", "MAXBYTESBEFOREFLUSH",
-        (uint64_t)tapebridge::TAPEBRIDGE_MAXBYTESBEFOREFLUSH, &m_log);
-      castorConf.tapebridgeMaxFilesBeforeFlush = config.getConfEntInt(
-        "TAPEBRIDGE", "MAXFILESBEFOREFLUSH",
-        (uint64_t)tapebridge::TAPEBRIDGE_MAXFILESBEFOREFLUSH, &m_log);
-      castorConf.tapeserverdDiskThreads = config.getConfEntInt(
-        "RTCPD", "THREAD_POOL", (uint32_t)RTCPD_THREAD_POOL, &m_log);
-      
-      rmc.reset(m_rmcFactory.create());
-      tapeserver.reset(m_tapeserverFactory.create(
-        DataTransferSession::getZmqContext()));
-      dataTransferSession.reset(new DataTransferSession (
-        m_hostName,
-        drive->getVdqmJob(),
-        m_log,
-        sysWrapper,
-        driveConfig,
-        *(rmc.get()),
-        *(tapeserver.get()),
-        m_capUtils,
-        castorConf
-      ));
-    } catch (castor::exception::Exception & ex) {
-      try {
-        client::ClientProxy cl(drive->getVdqmJob());
-        client::ClientInterface::RequestReport rep;
-        cl.reportEndOfSessionWithError(ex.getMessageValue(), ex.code(), rep);
-      } catch (...) {
-        params.push_back(log::Param("errorMessage", ex.getMessageValue()));
-        params.push_back(log::Param("errorCode", ex.code()));
-        m_log(LOG_ERR, "Failed to notify the client of the failed session"
-          " when setting up the data-transfer session", params);
-      }
-      throw;
-    } 
-    catch (...) {
-      try {
-        m_log(LOG_ERR, "Got non castor exception error while constructing"
-          " data-transfer session", params);
-        client::ClientProxy cl(drive->getVdqmJob());
-        client::ClientInterface::RequestReport rep;
-        cl.reportEndOfSessionWithError(
-         "Non-Castor exception when setting up the data-transfer session",
-           SEINTERNAL, rep);
-      } catch (...) {
-        params.push_back(log::Param("errorMessage",
-          "Non-Castor exception when setting up the data-transfer session"));
-        m_log(LOG_ERR, "Failed to notify the client of the failed session"
-          " when setting up the data-transfer session", params);
-      }
-      throw;
-    }
-    m_log(LOG_INFO, "Going to execute data-transfer session");
-    int result = dataTransferSession->execute();
-    exit(result);
-  } catch(castor::exception::Exception & ex) {
-    params.push_back(log::Param("message", ex.getMessageValue()));
-    m_log(LOG_ERR, "Aborting data-transfer session"
-      ": Caught an unexpected CASTOR exception", params);
-    castor::log::LogContext lc(m_log);
-    lc.logBacktrace(LOG_ERR, ex.backtrace());
-    exit(1);
-  } catch(std::exception &se) {
-    params.push_back(log::Param("message", se.what()));
-    m_log(LOG_ERR,
-      "Aborting data-transfer session: Caught an unexpected standard exception",
-      params);
-    exit(1);
-  } catch(...) {
-    m_log(LOG_ERR, "Aborting data-transfer session"
-      ": Caught an unexpected and unknown exception", params);
-    exit(1);
-  }
+// getDataTransferConf
+//------------------------------------------------------------------------------
+castor::tape::tapeserver::daemon::DataTransferSession::CastorConf
+  castor::tape::tapeserver::daemon::TapeDaemon::getDataTransferConf() {
+  DataTransferSession::CastorConf castorConf;
+  common::CastorConfiguration &config =
+    common::CastorConfiguration::getConfig();
+  castorConf.rtcopydBufsz = config.getConfEntInt(
+    "RTCOPYD", "BUFSZ", (uint32_t)RTCP_BUFSZ, &m_log);
+  castorConf.rtcopydNbBufs = config.getConfEntInt(
+    "RTCOPYD", "NB_BUFS", (uint32_t)NB_RTCP_BUFS, &m_log);
+  castorConf.tapeBadMIRHandlingRepair = config.getConfEntString(
+    "TAPE", "BADMIR_HANDLING", "CANCEL", &m_log);
+  castorConf.tapebridgeBulkRequestMigrationMaxBytes = config.getConfEntInt(
+    "TAPEBRIDGE", "BULKREQUESTMIGRATIONMAXBYTES",
+    (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTMIGRATIONMAXBYTES, &m_log);
+  castorConf.tapebridgeBulkRequestMigrationMaxFiles = config.getConfEntInt(
+    "TAPEBRIDGE", "BULKREQUESTMIGRATIONMAXFILES",
+    (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTMIGRATIONMAXFILES, &m_log);
+  castorConf.tapebridgeBulkRequestRecallMaxBytes = config.getConfEntInt(
+    "TAPEBRIDGE", "BULKREQUESTRECALLMAXBYTES",
+    (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTRECALLMAXBYTES, &m_log);
+  castorConf.tapebridgeBulkRequestRecallMaxFiles = config.getConfEntInt(
+    "TAPEBRIDGE", "BULKREQUESTRECALLMAXFILES",
+    (uint64_t)tapebridge::TAPEBRIDGE_BULKREQUESTRECALLMAXFILES, &m_log);
+  castorConf.tapebridgeMaxBytesBeforeFlush = config.getConfEntInt(
+    "TAPEBRIDGE", "MAXBYTESBEFOREFLUSH",
+    (uint64_t)tapebridge::TAPEBRIDGE_MAXBYTESBEFOREFLUSH, &m_log);
+  castorConf.tapebridgeMaxFilesBeforeFlush = config.getConfEntInt(
+    "TAPEBRIDGE", "MAXFILESBEFOREFLUSH",
+    (uint64_t)tapebridge::TAPEBRIDGE_MAXFILESBEFOREFLUSH, &m_log);
+  castorConf.tapeserverdDiskThreads = config.getConfEntInt(
+    "RTCPD", "THREAD_POOL", (uint32_t)RTCPD_THREAD_POOL, &m_log);
+
+  return castorConf;
 }
 
 //------------------------------------------------------------------------------
diff --git a/castor/tape/tapeserver/daemon/TapeDaemon.hpp b/castor/tape/tapeserver/daemon/TapeDaemon.hpp
index 7bacdecfca67a6c492ab0b0317e451a08bb6ac7b..51493bb48e7733f62e2071909bc0cb66bdbc1c1c 100644
--- a/castor/tape/tapeserver/daemon/TapeDaemon.hpp
+++ b/castor/tape/tapeserver/daemon/TapeDaemon.hpp
@@ -158,14 +158,145 @@ protected:
    */
   void forkProcessForker();
 
+  /**
+   * Socket pair used to control the ProcessForker.
+   */
+  struct ForkerCmdPair {
+
+    /**
+     * Bi-directional socket used by the TapeDaemon parent process to send
+     * commands to the process forker and receive replies in return.
+     */
+    int tapeDaemon;
+
+    /**
+     * Bi-directional socket used by the ProcessForker to receive commands
+     * from the TapeDaemon parent process and send back replies.
+     */
+    int processForker;
+
+    /**
+     * Constructor.
+     *
+     * This constructor sets both members to -1 which represents an invalid
+     * file descriptor.
+     */
+    ForkerCmdPair(): tapeDaemon(-1), processForker(-1) {
+    }
+  }; // struct ForkerCmdPair
+
+  /**
+   * Creates the socket pair to be used to control the ProcessForker.
+   *
+   * @return The socket pair.
+   */
+  ForkerCmdPair createForkerCmdPair();
+
+  /**
+   * Socket pair used by the ProcessForker to notify the TapeDaemon parent
+   * process of the termination of ProcessForker child processes.
+   */
+  struct ForkerReaperPair {
+
+    /**
+     * Socket used by the TapeDaemon receive process termination notifications
+     * from the ProcessForker.
+     */
+    int tapeDaemon;
+
+    /**
+     * Socket used by the ProcessForker to send process termination
+     * notifications to the TapeDaemon parent process.
+     */
+    int processForker;
+
+    /**
+     * Constructor.
+     *
+     * This constructor sets both members to -1 which represents an invalid
+     * file descriptor.
+     */
+    ForkerReaperPair(): tapeDaemon(-1), processForker(-1) {
+    }
+  }; // struct ForkerReaperPair
+
+  /**
+   * Creates the socket pair to be used by the ProcessForker to notify the
+   * TapeDaemon parent process of the termination of ProcessForker processes.
+   *
+   * @return The socket pair.
+   */
+  ForkerReaperPair createForkerReaperPair();
+
+  /**
+   * C++ wrapper around socketpair() that converts a failure into a C++
+   * exception.
+   *
+   * @return The socket pair.
+   */
+  std::pair<int, int> createSocketPair();
+
+  /**
+   * Closes both the sockets of the specified socket pair.
+   *
+   * @param cmdPair The socket pair to be close.
+   */
+  void closeForkerCmdPair(const ForkerCmdPair &cmdPair);
+
+  /**
+   * Closes both the sockets of the specified socket pair.
+   *
+   * @param reaperPair The socket pair to be close.
+   */
+  void closeForkerReaperPair(const ForkerReaperPair &reaperPair);
+
+  /**
+   * Acting on behalf of the TapeDaemon parent process this method closes the
+   * ProcessForker side of the socket pair used to control the ProcessForker.
+   *
+   * @param cmdPair The socket pair used to control the ProcessForker.
+   */
+  void closeProcessForkerSideOfCmdPair(const ForkerCmdPair &cmdPair);
+
+  /**
+   * Acting on behalf of the TapeDaemon parent process this method closes the
+   * ProcessForker side of the socket pair used by the ProcessForker to report
+   * process terminations.
+   *
+   * @param reaperPair The socket pair used by the ProcessForker to report
+   * process terminations.
+   */
+  void closeProcessForkerSideOfReaperPair(const ForkerReaperPair &reaperPair);
+
+  /**
+   * Acting on behalf of the ProcessForker process this method closes the
+   * TapeDaemon side of the socket pair used to control the ProcessForker.
+   *
+   * @param cmdPair The socket pair used to control the ProcessForker.
+   */
+  void closeTapeDaemonSideOfCmdPair(const ForkerCmdPair &cmdPair);
+
+  /**
+   * Acting on behalf of the ProcessForker process this method closes the
+   * TapeDaemon side of the socket pair used by the ProcessForker to report
+   * process terminations.
+   *
+   * @param reaperPair The socket pair used by the ProcessForker to report
+   * process terminations.
+   */
+  void closeTapeDaemonSideOfReaperPair(const ForkerReaperPair &reaperPair);
+
   /**
    * Runs the ProcessForker.
    *
    * @param cmdReceiverSocket The socket used to receive commands for the
    * ProcessForker.
+   * @param reaperSenderSocket The socket used to send process termination
+   * reports to the TapeDaemon parent process.
    * @return the exit code to be used for the process running the ProcessForker.
    */
-  int runProcessForker(const int cmdReceiverSocket) throw();
+  int runProcessForker(const int cmdReceiverSocket,
+    const int reaperSenderSocket) throw();
 
   /**
    * Blocks the signals that should not asynchronously disturb the daemon.
@@ -212,9 +343,18 @@ protected:
   void createAndRegisterLabelCmdAcceptHandler();
 
   /**
-   * Creates the handler to discuss through zmq socket to the forked sessions
+   * Creates the handler to handle messages from forked sessions.
    */
   void createAndRegisterTapeMessageHandler();
+
+  /**
+   * Creates the handler to handle the incoming connection from the
+   * ProcessForker.
+   *
+   * @param reaperSocket The TapeDaemon side of the socket pair used by the
+   * ProcessForker  to report the termination of its child processes.
+   */
+  void createAndRegisterProcessForkerConnectionHandler(const int reaperSocket);
   
   /**
    * The main event loop of the daemon.
@@ -287,15 +427,6 @@ protected:
    const pid_t pid,
    const int waitpidStat);
 
-  /**
-   * Does the required post processing for the specified reaped session.
-   *
-   * @param pid The process ID of the reaped session.
-   * @param waitpidStat The status information given by a call to waitpid().
-   */
-  void handleReapedDataTransferSession(const pid_t pid,
-    const int waitpidStat);
-  
   /**
    * Does the required post processing for the specified reaped session.
    *
@@ -375,13 +506,11 @@ protected:
   void forkDataTransferSession(DriveCatalogueEntry *drive) throw();
 
   /**
-   * Runs the data-transfer session.  This method is to be called within the
-   * child process responsible for running the data-transfer session.
+   * Gets the configuration of a data-transfer session.
    *
-   * @param drive The catalogue entry of the tape drive to be used during the
-   * session.
+   * @return The configuration.
    */
-  void runDataTransferSession(const DriveCatalogueEntry *drive) throw();
+  DataTransferSession::CastorConf getDataTransferConf();
 
   /**
    * Forks a label-session child-process for every tape drive entry in the
diff --git a/castor/tape/tapeserver/daemon/TapeMessageHandler.cpp b/castor/tape/tapeserver/daemon/TapeMessageHandler.cpp
index bbe73d7f6f22ee921feb44a3c8a03a6faea76332..2c42a7fe3689259044ae27dbde7df895d9a3c21f 100644
--- a/castor/tape/tapeserver/daemon/TapeMessageHandler.cpp
+++ b/castor/tape/tapeserver/daemon/TapeMessageHandler.cpp
@@ -96,29 +96,54 @@ void castor::tape::tapeserver::daemon::TapeMessageHandler::fillPollFd(
 // handleEvent
 //------------------------------------------------------------------------------
 bool castor::tape::tapeserver::daemon::TapeMessageHandler::handleEvent(
-  const zmq_pollitem_t &fd) {
-  checkSocket(fd);
-  m_log(LOG_DEBUG,"handling event in TapeMessageHandler");
-  messages::Header header; 
+  const zmq_pollitem_t &fd) throw() {
+  try {
+    checkSocket(fd);
+    m_log(LOG_INFO,"handling event in TapeMessageHandler");
   
-  try{
-    tape::utils::ZmqMsg headerBlob;
-    m_socket.recv(&headerBlob.getZmqMsg());
+    messages::Header header; 
+    try {
+      tape::utils::ZmqMsg headerBlob;
+      m_socket.recv(&headerBlob.getZmqMsg());
+    
+      if(!zmq_msg_more(&headerBlob.getZmqMsg())){
+        castor::exception::Exception ex;
+        ex.getMessage() << "No message body after reading the header";
+        throw ex;
+      }
     
-    if(!zmq_msg_more(&headerBlob.getZmqMsg())){
+      header = buildHeader(headerBlob);
+    } catch(castor::exception::Exception &ne){
       castor::exception::Exception ex;
-      ex.getMessage() <<"Read header blob, expecting a multi parts message but nothing to come";
+      ex.getMessage() << "Error handling message header: " <<
+        ne.getMessage().str();
+      throw ex;
+    }
+
+    tape::utils::ZmqMsg bodyBlob;
+    try {
+      m_socket.recv(&bodyBlob.getZmqMsg());
+    } catch(castor::exception::Exception &ne){
+      castor::exception::Exception ex;
+      ex.getMessage() << "Failed to receive message body: " << 
+        ne.getMessage().str();
       throw ex;
     }
-    
-    header = buildHeader(headerBlob);
-  }
-  catch(const castor::exception::Exception& ex){
-    m_log(LOG_ERR,"Error while dealing the message's header");
-    return false;
-  }
   
-  dispatchEvent(header);
+    dispatchMsgHandler(header, bodyBlob);
+
+  } catch(castor::exception::Exception &ex) {
+    log::Param params[] = {log::Param("message", ex.getMessage().str())};
+    m_log(LOG_ERR, "TapeMessageHandler failed to handle event", params);
+  } catch(std::exception &se) {
+    log::Param params[] = {log::Param("message", se.what())};
+    m_log(LOG_ERR, "TapeMessageHandler failed to handle event", params);
+  } catch(...) {
+    log::Param params[] = {
+      log::Param("message", "Caught an unknown exception")};
+    m_log(LOG_ERR, "TapeMessageHandler failed to handle event", params);
+  }
+
   return false; // Ask reactor to remove and delete this handler
 }
 
@@ -164,115 +189,133 @@ castor::messages::Header castor::tape::tapeserver::daemon::TapeMessageHandler::b
 }
 
 //------------------------------------------------------------------------------
-// dispatchEvent
+// dispatchMsgHandler
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeMessageHandler::dispatchEvent(
-  messages::Header& header) {
-  m_log(LOG_DEBUG,"dispatching  event in TapeMessageHandler");
-  tape::utils::ZmqMsg bodyBlob;
-  m_socket.recv(&bodyBlob.getZmqMsg());
+void castor::tape::tapeserver::daemon::TapeMessageHandler::dispatchMsgHandler(
+  messages::Header& header, const tape::utils::ZmqMsg &bodyBlob) {
+  m_log(LOG_INFO,"dispatching  event in TapeMessageHandler");
   
   switch(header.reqtype()){
-    case messages::reqType::Heartbeat:
+  case messages::reqType::Heartbeat:
+    return handleHeartbeatMsg(header, bodyBlob);
+  case messages::reqType::NotifyDriveBeforeMountStarted:
+    return handleNotifyDriveBeforeMountStartedMsg(header, bodyBlob);
+  case messages::reqType::NotifyDriveTapeMounted:
+    return handleNotifyDriveTapeMountedMsg(header, bodyBlob);
+  case messages::reqType::NotifyDriveTapeUnmounted:
+    return handleNotifyDriveTapeUnmountedMsg(header, bodyBlob);
+  case messages::reqType::NotifyDriveUnmountStarted:
+    return handleNotifyDriveUnmountStartedMsg(header, bodyBlob);
+  default:
     {
-      castor::messages::Heartbeat body;
-      unserialize(body,bodyBlob);
-      castor::messages::checkSHA1(header,bodyBlob);
-      dealWith(header,body);
-    }
-      break;
-    case messages::reqType::NotifyDriveBeforeMountStarted:
-    {
-      castor::messages::NotifyDriveBeforeMountStarted body;
-      unserialize(body,bodyBlob);
-      castor::messages::checkSHA1(header,bodyBlob);
-      dealWith(header,body);
-    }
-      break;
-    case messages::reqType::NotifyDriveTapeMounted:
-    {
-      castor::messages::NotifyDriveTapeMounted body;
-      unserialize(body,bodyBlob);
-      castor::messages::checkSHA1(header,bodyBlob);
-      dealWith(header,body);
+      castor::exception::Exception ex;
+      ex.getMessage() << "Failed to dispatch message handler"
+        ": Unknown request type: reqtype=" << header.reqtype();
+      throw ex;
     }
-      break;
-    case messages::reqType::NotifyDriveTapeUnmounted:
-      sendSuccessReplyToClient();
-      break;
-    case messages::reqType::NotifyDriveUnmountStarted:
-      sendSuccessReplyToClient();
-      break;
-    default:
-      m_log(LOG_ERR,"default  dispatch in TapeMessageHandler");
-      break;
   }
 }
 
 //------------------------------------------------------------------------------
-// dealWith
+// handleHeartbeatMsg
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeMessageHandler::dealWith(
-const castor::messages::Header& header, const castor::messages::Heartbeat& body){
+void castor::tape::tapeserver::daemon::TapeMessageHandler::handleHeartbeatMsg(
+  const messages::Header& header, const tape::utils::ZmqMsg &bodyBlob) {
+  m_log(LOG_INFO, "Handling Heartbeat message");
+
+  try {
+    castor::messages::Heartbeat body;
+    parseMsgBlob(body, bodyBlob);
+    castor::messages::checkSHA1(header, bodyBlob);
   
-  std::vector<log::Param> param;
-  param.push_back(log::Param("bytesMoved",body.bytesmoved()));
-  m_log(LOG_DEBUG,"IT IS ALIVE",param);
+    std::vector<log::Param> param;
+    param.push_back(log::Param("bytesMoved",body.bytesmoved()));
   
-  sendSuccessReplyToClient();
+    sendSuccessReplyToClient();
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle Heartbeat message: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
 }
 
-void castor::tape::tapeserver::daemon::TapeMessageHandler::dealWith(
-const castor::messages::Header& header, 
-const castor::messages::NotifyDriveBeforeMountStarted& body){
-  m_log(LOG_INFO,"NotifyDriveBeforeMountStarted-dealWith");
-  //check castor consistensy
-  if(body.mode()==castor::messages::TAPE_MODE_READWRITE) {
-    legacymsg::VmgrTapeInfoMsgBody tapeInfo;
-    m_vmgr.queryTape(body.vid(), tapeInfo);
-    // If the client is the tape gateway and the volume is not marked as BUSY
-    if(body.clienttype() == castor::messages::NotifyDriveBeforeMountStarted::CLIENT_TYPE_GATEWAY 
-            && !(tapeInfo.status & TAPE_BUSY)) {
-      castor::exception::Exception ex;
-      ex.getMessage() << "The tape gateway is the client and the tape to be mounted is not BUSY: vid=" << body.vid();
+//------------------------------------------------------------------------------
+// handleNotifyDriveBeforeMountStartedMsg
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeMessageHandler::
+  handleNotifyDriveBeforeMountStartedMsg(const messages::Header& header, 
+  const tape::utils::ZmqMsg &bodyBlob) {
+  m_log(LOG_INFO, "Handling NotifyDriveBeforeMountStarted message");
+
+  try {
+    castor::messages::NotifyDriveBeforeMountStarted body;
+    parseMsgBlob(body, bodyBlob);
+    castor::messages::checkSHA1(header, bodyBlob);
+
+    //check castor consistensy
+    if(body.mode()==castor::messages::TAPE_MODE_READWRITE) {
+      legacymsg::VmgrTapeInfoMsgBody tapeInfo;
+      m_vmgr.queryTape(body.vid(), tapeInfo);
+      // If the client is the tape gateway and the volume is not marked as BUSY
+      if(body.clienttype() == castor::messages::NotifyDriveBeforeMountStarted::CLIENT_TYPE_GATEWAY 
+        && !(tapeInfo.status & TAPE_BUSY)) {
+        castor::exception::Exception ex;
+        ex.getMessage() <<
+          "The tape gateway is the client and the tape to be mounted is not BUSY"
+          ": vid=" << body.vid();
       
-      //send an error to the client
-      sendErrorReplyToClient(ex);
-      throw ex;
-    }
+        //  send an error to the client
+        sendErrorReplyToClient(ex);
+        throw ex;
+      }
     
-    castor::messages::NotifyDriveBeforeMountStartedAnswer body;
-    body.set_howmanyfilesontape(tapeInfo.nbFiles);
+      castor::messages::NotifyDriveBeforeMountStartedAnswer body;
+      body.set_howmanyfilesontape(tapeInfo.nbFiles);
     
-    castor::messages::Header header = castor::messages::preFillHeader();
-    header.set_reqtype(messages::reqType::NotifyDriveBeforeMountStartedAnswer);
-    header.set_bodyhashvalue(castor::messages::computeSHA1Base64(body));
-    header.set_bodysignature("PIPO");
+      castor::messages::Header header = castor::messages::preFillHeader();
+      header.set_reqtype(messages::reqType::NotifyDriveBeforeMountStartedAnswer);
+      header.set_bodyhashvalue(castor::messages::computeSHA1Base64(body));
+      header.set_bodysignature("PIPO");
     
-    //send the number of files on the tape to the client
-    castor::messages::sendMessage(m_socket,header,ZMQ_SNDMORE);
-    castor::messages::sendMessage(m_socket,body);
-  } else {
-    //we were not event in castor::messages::TAPE_MODE_READWRITE mpde
-    //so send empty answer
-    sendSuccessReplyToClient();
+      //send the number of files on the tape to the client
+      castor::messages::sendMessage(m_socket,header,ZMQ_SNDMORE);
+      castor::messages::sendMessage(m_socket,body);
+    } else {
+      //we were not event in castor::messages::TAPE_MODE_READWRITE mpde
+      //so send empty answer
+      sendSuccessReplyToClient();
+    }
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() <<
+      "Failed to handle NotifyDriveBeforeMountStarted message: " <<
+      ne.getMessage().str();
+    throw ex;
   }
 }
+
 //------------------------------------------------------------------------------
-// dealWith
+// handleNotifyDriveTapeMountedMsg
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeMessageHandler::dealWith(
-const castor::messages::Header& header, 
-const castor::messages::NotifyDriveTapeMounted& body){
-  m_log(LOG_INFO,"NotifyDriveTapeMounted-dealWith");
-  DriveCatalogueEntry *const drive = m_driveCatalogue.findDrive(body.unitname());
-  drive->updateVolumeInfo(body);
-  const utils::DriveConfig &driveConfig = drive->getConfig();
+void castor::tape::tapeserver::daemon::TapeMessageHandler::
+  handleNotifyDriveTapeMountedMsg(const messages::Header& header, 
+  const tape::utils::ZmqMsg &bodyBlob) {
+  m_log(LOG_INFO, "Handling NotifyDriveTapeMounted message");
+
+  try {
+    castor::messages::NotifyDriveTapeMounted body;
+    parseMsgBlob(body, bodyBlob);
+    castor::messages::checkSHA1(header, bodyBlob);
+
+    DriveCatalogueEntry *const drive =
+      m_driveCatalogue.findDrive(body.unitname());
+    drive->updateVolumeInfo(body);
+    const utils::DriveConfig &driveConfig = drive->getConfig();
     
-  const std::string vid = body.vid();
-  try
-  {
-    switch(body.mode()) {
+    const std::string vid = body.vid();
+    try {
+      switch(body.mode()) {
       case castor::messages::TAPE_MODE_READ:
         m_vmgr.tapeMountedForRead(vid, drive->getSessionPid());
         break;
@@ -288,22 +331,67 @@ const castor::messages::NotifyDriveTapeMounted& body){
         castor::exception::Exception ex;
         ex.getMessage() << "Unknown tape mode: " << body.mode();
         throw ex;
+      }
+      m_vdqm.tapeMounted(m_hostName, body.unitname(), driveConfig.dgn,
+        body.vid(), drive->getSessionPid());
+    } catch(const castor::exception::Exception& ex) {
+      sendErrorReplyToClient(ex);
+      throw;
     }
-    m_vdqm.tapeMounted(m_hostName, body.unitname(), driveConfig.dgn, body.vid(),
-            drive->getSessionPid());
-  }catch(const castor::exception::Exception& ex){
-    sendErrorReplyToClient(ex);
-    throw;
+    sendSuccessReplyToClient();
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle NotifyDriveTapeMounted message: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
+//------------------------------------------------------------------------------
+// handleNotifyDriveTapeUnmountedMsg
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeMessageHandler::
+  handleNotifyDriveTapeUnmountedMsg(const messages::Header& header,
+  const tape::utils::ZmqMsg &bodyBlob) {
+  m_log(LOG_INFO, "Handling NotifyDriveTapeUnmounted message");
+
+  try {
+    sendSuccessReplyToClient();
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle NotifyDriveTapeUnmounted message: " <<
+      ne.getMessage().str();
+    throw ex;
   }
-  sendSuccessReplyToClient();
 }
+
+//------------------------------------------------------------------------------
+// handleNotifyDriveUnmountStartedMsg
+//------------------------------------------------------------------------------
+void castor::tape::tapeserver::daemon::TapeMessageHandler::
+  handleNotifyDriveUnmountStartedMsg(const messages::Header& header,
+  const tape::utils::ZmqMsg &bodyBlob) {
+  m_log(LOG_INFO, "Handling handleNotifyDriveUnmountStarted message");
+
+  try {
+    sendSuccessReplyToClient();
+  } catch(castor::exception::Exception &ne) {
+    castor::exception::Exception ex;
+    ex.getMessage() << "Failed to handle NotifyDriveUnmountStarted message: " <<
+      ne.getMessage().str();
+    throw ex;
+  }
+}
+
 //------------------------------------------------------------------------------
 // sendSuccessReplyToClient
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeMessageHandler::sendSuccessReplyToClient(){
-    castor::messages::ReturnValue body;
-    sendReplyToClient(0,"");
+void castor::tape::tapeserver::daemon::TapeMessageHandler::
+  sendSuccessReplyToClient() {
+  castor::messages::ReturnValue body;
+  sendReplyToClient(0,"");
 }
+
 //------------------------------------------------------------------------------
 // sendErrorReplyToClient
 //------------------------------------------------------------------------------
@@ -312,20 +400,21 @@ sendErrorReplyToClient(const castor::exception::Exception& ex){
   //any positive value will trigger an exception in the client side
   sendReplyToClient(ex.code(),ex.getMessageValue());
 }
+
 //------------------------------------------------------------------------------
 // sendReplyToClient
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::TapeMessageHandler::
-sendReplyToClient(int returnValue,const std::string& msg){
-    castor::messages::ReturnValue body;
-    body.set_returnvalue(returnValue);
-    body.set_message(msg);
+void castor::tape::tapeserver::daemon::TapeMessageHandler::sendReplyToClient(
+  const int returnValue, const std::string& msg) {
+  castor::messages::ReturnValue body;
+  body.set_returnvalue(returnValue);
+  body.set_message(msg);
   
-    castor::messages::Header header = castor::messages::preFillHeader();
-    header.set_reqtype(messages::reqType::ReturnValue);
-    header.set_bodyhashvalue(castor::messages::computeSHA1Base64(body));
-    header.set_bodysignature("PIPO");
+  messages::Header header = castor::messages::preFillHeader();
+  header.set_reqtype(messages::reqType::ReturnValue);
+  header.set_bodyhashvalue(messages::computeSHA1Base64(body));
+  header.set_bodysignature("PIPO");
 
-    castor::messages::sendMessage(m_socket,header,ZMQ_SNDMORE);
-    castor::messages::sendMessage(m_socket,body);
+  messages::sendMessage(m_socket, header,ZMQ_SNDMORE);
+  messages::sendMessage(m_socket, body);
 }
diff --git a/castor/tape/tapeserver/daemon/TapeMessageHandler.hpp b/castor/tape/tapeserver/daemon/TapeMessageHandler.hpp
index 5d88a984f5251aef0021d9fcc65ea0753b4d9290..fad224c97f61b9f1d6052f01dbdf34c9d1df0c0b 100644
--- a/castor/tape/tapeserver/daemon/TapeMessageHandler.hpp
+++ b/castor/tape/tapeserver/daemon/TapeMessageHandler.hpp
@@ -56,6 +56,7 @@ public:
    * @param driveCatalogue The tape-drive catalogue.
    * @param hostName The name of the host.
    * @param vdqm Proxy object representing the vdqmd daemon.
+   * @param vmgr Proxy object representing the vmgrd daemon.
    * @param zmqContext The ZMQ context.
    */
   TapeMessageHandler(
@@ -90,7 +91,7 @@ public:
    * @return true if the event handler should be removed from and deleted by
    * the reactor.
    */
-  bool handleEvent(const zmq_pollitem_t &fd);
+  bool handleEvent(const zmq_pollitem_t &fd) throw();
   
 private:
   /**
@@ -117,12 +118,14 @@ private:
    * @param msg
    * @param blob
    */
-  template <class T> void unserialize(T& msg, tape::utils::ZmqMsg& blob){
-    std::string logMessage="Cant parse " ;
-    logMessage+=castor::utils::demangledNameOf(msg)+" from binary data. Wrong body";
-    if(!msg.ParseFromArray(zmq_msg_data(&blob.getZmqMsg()),zmq_msg_size(&blob.getZmqMsg()))){
-        m_log(LOG_ERR,logMessage); 
-      }
+  template <class T> void parseMsgBlob(T& msg, const tape::utils::ZmqMsg& blob) {
+    if(!msg.ParseFromArray(blob.data(), blob.size())) {
+      castor::exception::Exception ex;
+      ex.getMessage() << "Failed to parse a " <<
+        castor::utils::demangledNameOf(msg) << " message blob"
+        ": ParseFromArray() returned false";
+      throw ex;
+    }
   }
   
    /**
@@ -167,19 +170,58 @@ private:
   void checkSocket(const zmq_pollitem_t &fd);
   
   /**
-   * Call the right dealWith according to header.reqType()
-   * @param header
+   * Dispatches the appropriate handler method for the specified message.
+   *
+   * @param header The header of the message.
+   * @param bodyBlob The serialized body of the message.
    */
-  void dispatchEvent(castor::messages::Header& header);
-  
-  void dealWith(const castor::messages::Header&,
-                         const castor::messages::Heartbeat& body);
-  
-  void dealWith(const castor::messages::Header& header, 
-     const castor::messages::NotifyDriveBeforeMountStarted& body);
+  void dispatchMsgHandler(castor::messages::Header& header,
+    const tape::utils::ZmqMsg &bodyBlob);
+
+  /**
+   * Handles the specified message.
+   *
+   * @param header The header of the message.
+   * @param bodyBlob The serialized body of the message.
+   */
+  void handleHeartbeatMsg(const messages::Header& header, 
+    const tape::utils::ZmqMsg &bodyBlob);
+
+  /**
+   * Handles the specified message.
+   *
+   * @param header The header of the message.
+   * @param bodyBlob The serialized body of the message.
+   */
+  void handleNotifyDriveBeforeMountStartedMsg(const messages::Header& header,
+    const tape::utils::ZmqMsg &bodyBlob);
 
-  void dealWith(const castor::messages::Header& header,
-     const castor::messages::NotifyDriveTapeMounted& body); 
+  /**
+   * Handles the specified message.
+   *
+   * @param header The header of the message.
+   * @param bodyBlob The serialized body of the message.
+   */
+  void handleNotifyDriveTapeMountedMsg(const messages::Header& header,
+    const tape::utils::ZmqMsg &bodyBlob);
+
+  /**
+   * Handles the specified message.
+   *
+   * @param header The header of the message.
+   * @param bodyBlob The serialized body of the message.
+   */
+  void handleNotifyDriveTapeUnmountedMsg(const messages::Header& header,
+    const tape::utils::ZmqMsg &bodyBlob);
+
+  /**
+   * Handles the specified message.
+   *
+   * @param header The header of the message.
+   * @param bodyBlob The serialized body of the message.
+   */
+  void handleNotifyDriveUnmountStartedMsg(const messages::Header& header,
+    const tape::utils::ZmqMsg &bodyBlob);
 
   /**
    * Unserialize the blob and check the header
diff --git a/castor/tape/tapeserver/daemon/VdqmConnectionHandler.cpp b/castor/tape/tapeserver/daemon/VdqmConnectionHandler.cpp
index e3a600f52ca8d122d6281a2e79fce2c601490ea9..8127a5b1e51db752f4a6d1c84fe1e413dfaaad4f 100644
--- a/castor/tape/tapeserver/daemon/VdqmConnectionHandler.cpp
+++ b/castor/tape/tapeserver/daemon/VdqmConnectionHandler.cpp
@@ -71,7 +71,7 @@ void castor::tape::tapeserver::daemon::VdqmConnectionHandler::fillPollFd(zmq_pol
 //------------------------------------------------------------------------------
 bool castor::tape::tapeserver::daemon::VdqmConnectionHandler::handleEvent(
   const zmq_pollitem_t &fd)  {
-  logVdqmConnectionEvent(fd);
+  logConnectionEvent(fd);
 
   checkHandleEventFd(fd.fd);
 
@@ -99,10 +99,10 @@ bool castor::tape::tapeserver::daemon::VdqmConnectionHandler::handleEvent(
 }
 
 //------------------------------------------------------------------------------
-// logVdqmConnectionEvent 
+// logConnectionEvent 
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::VdqmConnectionHandler::
-  logVdqmConnectionEvent(const zmq_pollitem_t &fd)  {
+  logConnectionEvent(const zmq_pollitem_t &fd)  {
   log::Param params[] = {
   log::Param("fd", fd.fd),
   log::Param("ZMQ_POLLIN", fd.revents & ZMQ_POLLIN ? "true" : "false"),
diff --git a/castor/tape/tapeserver/daemon/VdqmConnectionHandler.hpp b/castor/tape/tapeserver/daemon/VdqmConnectionHandler.hpp
index f58b8cbf220619a5a234ca6fe4f88d55bcef6c6d..0096cd8cddf5bebd28c67271c17d54908d3741d5 100644
--- a/castor/tape/tapeserver/daemon/VdqmConnectionHandler.hpp
+++ b/castor/tape/tapeserver/daemon/VdqmConnectionHandler.hpp
@@ -34,8 +34,6 @@
 #include "h/vdqm_constants.h"
 #include "h/rtcp_constants.h"
 
-#include <poll.h>
-
 namespace castor     {
 namespace tape       {
 namespace tapeserver {
@@ -70,7 +68,7 @@ public:
 
   /**
    * Fills the specified poll file-descriptor ready to be used in a call to
-   * poll().
+   * zmq_poll().
    */
   void fillPollFd(zmq_pollitem_t &fd) throw();
 
@@ -118,8 +116,10 @@ private:
 
   /**
    * Logs the specifed IO event of the vdqm connection.
+   *
+   * @param fd File descriptor describing the event.
    */
-  void logVdqmConnectionEvent(const zmq_pollitem_t &fd);
+  void logConnectionEvent(const zmq_pollitem_t &fd);
 
   /**
    * Throws an exception if the specified file-descriptor is not that of the
diff --git a/castor/tape/utils/CMakeLists.txt b/castor/tape/utils/CMakeLists.txt
index 624ffffb0624fd9b8446c2fdcdcf9f13c1aea568..65e839016242653bffb3e1df4c116c24a8b744f3 100644
--- a/castor/tape/utils/CMakeLists.txt
+++ b/castor/tape/utils/CMakeLists.txt
@@ -29,6 +29,7 @@ set (TAPE_UTILS_LIB_SRC_FILES
   BoolFunctor.cpp
   DriveConfigMap.cpp
   ShutdownBoolFunctor.cpp
+  SmartZmqContext.cpp
   Timer.cpp
   TpconfigLine.cpp
   utils.cpp
diff --git a/castor/tape/utils/SmartZmqContext.cpp b/castor/tape/utils/SmartZmqContext.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c46c566befc9761685d59f2f83bd81c9a23bd1ec
--- /dev/null
+++ b/castor/tape/utils/SmartZmqContext.cpp
@@ -0,0 +1,103 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *
+ *
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#include "castor/tape/utils/SmartZmqContext.hpp"
+
+#include <errno.h>
+#include <unistd.h>
+#include <zmq.h>
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+castor::tape::utils::SmartZmqContext::SmartZmqContext() throw() :
+  m_zmqContext(NULL) {
+}
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+castor::tape::utils::SmartZmqContext::SmartZmqContext(void *const zmqContext)
+  throw() : m_zmqContext(zmqContext) {
+}
+
+//-----------------------------------------------------------------------------
+// reset
+//-----------------------------------------------------------------------------
+void castor::tape::utils::SmartZmqContext::reset(void *const zmqContext)
+  throw() {
+  // If the new ZMQ context is not the one already owned
+  if(zmqContext != m_zmqContext) {
+
+    // If this smart pointer still owns a ZMQ context, then terminate it
+    if(m_zmqContext != NULL) {
+      zmq_term(m_zmqContext);
+    }
+
+    // Take ownership of the new ZMQ context
+    m_zmqContext = zmqContext;
+  }
+}
+
+//-----------------------------------------------------------------------------
+// SmartZmqContext assignment operator
+//-----------------------------------------------------------------------------
+castor::tape::utils::SmartZmqContext
+  &castor::tape::utils::SmartZmqContext::operator=(SmartZmqContext& obj) {
+  reset(obj.release());
+  return *this;
+}
+
+//-----------------------------------------------------------------------------
+// destructor
+//-----------------------------------------------------------------------------
+castor::tape::utils::SmartZmqContext::~SmartZmqContext() throw() {
+  reset();
+}
+
+//-----------------------------------------------------------------------------
+// get
+//-----------------------------------------------------------------------------
+void *castor::tape::utils::SmartZmqContext::get() const throw() {
+  return m_zmqContext;
+}
+
+//-----------------------------------------------------------------------------
+// release
+//-----------------------------------------------------------------------------
+void *castor::tape::utils::SmartZmqContext::release() {
+  // If this smart pointer does not own a ZMQ context
+  if(NULL == m_zmqContext) {
+    castor::exception::NotAnOwner ex;
+    ex.getMessage() << "Smart pointer does not own a ZMQ context";
+    throw ex;
+  }
+
+  void *const tmp = m_zmqContext;
+
+  // A NULL value indicates this smart pointer does not own a ZMQ context
+  m_zmqContext = NULL;
+
+  return tmp;
+}
diff --git a/castor/tape/utils/SmartZmqContext.hpp b/castor/tape/utils/SmartZmqContext.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8be23a59f4f631dd8057b8344f1528e987442774
--- /dev/null
+++ b/castor/tape/utils/SmartZmqContext.hpp
@@ -0,0 +1,124 @@
+/******************************************************************************
+ *
+ * This file is part of the Castor project.
+ * See http://castor.web.cern.ch/castor
+ *
+ * Copyright (C) 2003  CERN
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * 
+ * @author Castor Dev team, castor-dev@cern.ch
+ *****************************************************************************/
+
+#pragma once
+
+#include "castor/exception/NotAnOwner.hpp"
+
+#include <stdio.h>
+
+
+namespace castor {
+namespace tape {
+namespace utils {
+
+/**
+ * A smart pointer that owns a ZMQ context.  If the smart pointer goes out of
+ * scope and it owns a ZMQ context, then it will terminate that context by
+ * calling zmq_term().
+ */
+class SmartZmqContext {
+
+public:
+
+  /**
+   * Constructor.
+   */
+  SmartZmqContext() throw();
+
+  /**
+   * Constructor.
+   *
+   * @param zmqContext The ZMQ context to be owned by the smart pointer.
+   */
+  SmartZmqContext(void *const zmqContext) throw();
+
+  /**
+   * Take ownership of the specified ZMQ context, terminating the previously
+   * owned ZMQ context if there is one and it is not the same as the one
+   * specified.
+   *
+   * @param zmqContext The ZMQ context to be owned, defaults to NULL if not
+   * specified, where NULL means this smart pointer will not own a ZMQ context
+   * after the reset() method returns.
+   */
+  void reset(void *const zmqContext = NULL) throw();
+
+  /**
+   * SmartZmqContext assignment operator.
+   *
+   * This function does the following:
+   * <ul>
+   * <li> Calls release on the previous owner (obj);
+   * <li> Terminates the ZMQ context of this object if it already owns one.
+   * <li> Makes this object the owner of the ZMQ context released from the
+   *      previous owner (obj).
+   * </ul>
+   */
+  SmartZmqContext &operator=(SmartZmqContext& obj);
+
+  /**
+   * Destructor.
+   *
+   * If the smart pointer owns a ZMQ context, then the destructor will
+   * terminate it by calling zmq_term().
+   */
+  ~SmartZmqContext() throw();
+
+  /**
+   * Returns the owned ZMQ context or NULL if this smart pointer does not own
+   * one.
+   *
+   * @return The owned ZMQ context or NULL.
+   */
+  void *get() const throw();
+
+  /**
+   * Releases the owned ZMQ context.
+   *
+   * @return The released ZMQ context.
+   */
+  void *release() ;
+
+private:
+
+  /**
+   * The owned ZMQ context.  A value of NULL means this smart pointer does not
+   * own a ZMQ context.
+   */ 
+  void *m_zmqContext;
+
+  /**
+   * Private copy-constructor to prevent users from trying to create a new
+   * copy of an object of this class.
+   *
+   * Not implemented so that it cannot be called.
+   */
+  SmartZmqContext(const SmartZmqContext &obj) throw();
+
+}; // class SmartZmqContext
+
+} // namespace utils
+} // namespace tape
+} // namespace castor
+
diff --git a/castor/utils/SmartFILEPtr.cpp b/castor/utils/SmartFILEPtr.cpp
index 6bb04b68ed8335845b4081c9a060418d63dd5f41..230531cf2fb5719046fb199f49694b2e2a449d8e 100644
--- a/castor/utils/SmartFILEPtr.cpp
+++ b/castor/utils/SmartFILEPtr.cpp
@@ -44,8 +44,7 @@ castor::utils::SmartFILEPtr::SmartFILEPtr(FILE *const file) throw() :
 //-----------------------------------------------------------------------------
 // reset
 //-----------------------------------------------------------------------------
-void castor::utils::SmartFILEPtr::reset(FILE *const file = NULL)
-   throw() {
+void castor::utils::SmartFILEPtr::reset(FILE *const file) throw() {
   // If the new pointer is not the one already owned
   if(file != m_file) {
 
@@ -62,9 +61,8 @@ void castor::utils::SmartFILEPtr::reset(FILE *const file = NULL)
 //-----------------------------------------------------------------------------
 // SmartFILEPtr assignment operator
 //-----------------------------------------------------------------------------
-castor::utils::SmartFILEPtr
-  &castor::utils::SmartFILEPtr::operator=(SmartFILEPtr& obj)
-   {
+castor::utils::SmartFILEPtr &castor::utils::SmartFILEPtr::operator=(
+  SmartFILEPtr& obj) {
   reset(obj.release());
   return *this;
 }
@@ -86,10 +84,9 @@ FILE *castor::utils::SmartFILEPtr::get() const throw() {
 //-----------------------------------------------------------------------------
 // release
 //-----------------------------------------------------------------------------
-FILE *castor::utils::SmartFILEPtr::release()
-   {
+FILE *castor::utils::SmartFILEPtr::release() {
   // If this smart pointer does not own a pointer
-  if(m_file == NULL) {
+  if(NULL == m_file) {
     castor::exception::NotAnOwner ex;
     ex.getMessage() << "Smart pointer does not own a FILE pointer";
     throw ex;
diff --git a/castor/utils/SmartFILEPtr.hpp b/castor/utils/SmartFILEPtr.hpp
index 9c41796bed5b22f8321ce1b8616054608bf71b4d..9003e0ff3117f19c3bbd4de168c6f0a287a457a6 100644
--- a/castor/utils/SmartFILEPtr.hpp
+++ b/castor/utils/SmartFILEPtr.hpp
@@ -60,7 +60,7 @@ public:
    *             specified, where NULL means this smart pointer will not own a
    *             pointer after the reset() method returns.
    */
-  void reset(FILE *const file) throw();
+  void reset(FILE *const file = NULL) throw();
 
   /**
    * SmartFILEPtr assignment operator.
@@ -73,8 +73,7 @@ public:
    *      previous owner (obj).
    * </ul>
    */
-  SmartFILEPtr &operator=(SmartFILEPtr& obj)
-    ;
+  SmartFILEPtr &operator=(SmartFILEPtr& obj);
 
   /**
    * Destructor.