src/log/Makefile.am              |    8 +-
 src/log/config/logsv_classes.xml |    7 +-
 src/log/logd/lgs_config.cc       |  193 +++++++--
 src/log/logd/lgs_config.h        |    3 +-
 src/log/logd/lgs_dest.cc         |  746 +++++++++++++++++++++++++++++++++++++++
 src/log/logd/lgs_dest.h          |  580 ++++++++++++++++++++++++++++++
 src/log/logd/lgs_evt.cc          |   33 +
 src/log/logd/lgs_imm.cc          |  140 ++++++-
 src/log/logd/lgs_mbcsv.cc        |   91 ++++-
 src/log/logd/lgs_mbcsv.h         |    6 +-
 src/log/logd/lgs_mbcsv_v7.cc     |  177 +++++++++
 src/log/logd/lgs_mbcsv_v7.h      |   67 +++
 src/log/logd/lgs_stream.cc       |   54 ++-
 src/log/logd/lgs_stream.h        |   16 +
 14 files changed, 2041 insertions(+), 80 deletions(-)


Here are major info, detailed info will be added to PR doc soon.
1) Add attribute "saLogRecordDestination" to log stream.
2) Add Local socket destintion handler
3) Integrate into first increment made by Lennart

diff --git a/src/log/Makefile.am b/src/log/Makefile.am
--- a/src/log/Makefile.am
+++ b/src/log/Makefile.am
@@ -71,9 +71,11 @@ noinst_HEADERS += \
        src/log/logd/lgs_mbcsv_v2.h \
        src/log/logd/lgs_mbcsv_v3.h \
        src/log/logd/lgs_mbcsv_v5.h \
+       src/log/logd/lgs_mbcsv_v7.h \
        src/log/logd/lgs_recov.h \
        src/log/logd/lgs_stream.h \
-       src/log/logd/lgs_util.h
+       src/log/logd/lgs_util.h \
+       src/log/logd/lgs_dest.h
 
 bin_PROGRAMS += bin/saflogger
 osaf_execbin_PROGRAMS += bin/osaflogd
@@ -117,10 +119,12 @@ bin_osaflogd_SOURCES = \
        src/log/logd/lgs_mbcsv_v2.cc \
        src/log/logd/lgs_mbcsv_v3.cc \
        src/log/logd/lgs_mbcsv_v5.cc \
+       src/log/logd/lgs_mbcsv_v7.cc \
        src/log/logd/lgs_mds.cc \
        src/log/logd/lgs_recov.cc \
        src/log/logd/lgs_stream.cc \
-       src/log/logd/lgs_util.cc
+       src/log/logd/lgs_util.cc \
+       src/log/logd/lgs_dest.cc
 
 bin_osaflogd_LDADD = \
        lib/libosaf_common.la \
diff --git a/src/log/config/logsv_classes.xml b/src/log/config/logsv_classes.xml
--- a/src/log/config/logsv_classes.xml
+++ b/src/log/config/logsv_classes.xml
@@ -147,12 +147,13 @@
                        <category>SA_CONFIG</category>
                        <flag>SA_WRITABLE</flag>
                </attr>
-               <attr>
+               <attr>
                        <name>saLogRecordDestination</name>
-                       <type>SA_UINT32_T</type>
+                       <type>SA_STRING_T</type>
                        <category>SA_CONFIG</category>
                        <flag>SA_WRITABLE</flag>
-                        <flag>SA_MULTI_VALUE</flag>
+                       <flag>SA_MULTI_VALUE</flag>
+                       <flag>SA_NO_DUPLICATES</flag>
                </attr>
                <attr>
                        <name>saLogStreamCreationTimestamp</name>
diff --git a/src/log/logd/lgs_config.cc b/src/log/logd/lgs_config.cc
--- a/src/log/logd/lgs_config.cc
+++ b/src/log/logd/lgs_config.cc
@@ -40,6 +40,7 @@
 #include "osaf/immutil/immutil.h"
 #include "log/logd/lgs_file.h"
 #include "log/logd/lgs.h"
+#include "log/logd/lgs_dest.h"
 
 static SaVersionT immVersion = { 'A', 2, 11 };
 
@@ -117,10 +118,6 @@ typedef struct _lgs_conf_t {
   std::vector<std::string> logRecordDestinationConfiguration; // Default empty
   /* --- end correspond to IMM Class --- */
 
-  /* --- Used with OpenSafLogCurrentConfig runtime object only --- */
-  /* Note: Has no cnfflag */
-  std::vector<std::string> logRecordDestinationStatus; // Default empty
-
   /* Used for checkpointing time when files are closed */
   time_t chkp_file_close_time;
 
@@ -254,6 +251,9 @@ void lgs_cfgupd_multival_add(const std::
                              const std::vector<std::string>& value_list,
                              lgs_config_chg_t *config_data) {
   TRACE_ENTER();
+
+  CfgDestination(value_list, ModifyType::kAdd);
+
   // Get the existing multi-values and add them to the config data list
   lgs_logconfGet_t param_id = param_name_to_id(attribute_name);
   const std::vector<std::string> *exist_list =
@@ -294,6 +294,9 @@ void lgs_cfgupd_multival_delete(std::str
                                 std::vector<std::string> value_list,
                                 lgs_config_chg_t *config_data) {
   TRACE_ENTER();
+
+  CfgDestination(value_list, ModifyType::kDelete);
+
   // Get the existing multi-values
   lgs_logconfGet_t param_id = param_name_to_id(attribute_name);
   const std::vector<std::string> *exist_list =
@@ -325,6 +328,9 @@ void lgs_cfgupd_mutival_replace(std::str
                                 std::vector<std::string> value_list,
                                 lgs_config_chg_t *config_data) {
   TRACE_ENTER();
+
+  CfgDestination(value_list, ModifyType::kReplace);
+
   // Add given value-list to the config data list
   for (auto& value : value_list) {
     lgs_cfgupd_list_create(attribute_name.c_str(),
@@ -373,7 +379,6 @@ int lgs_cfg_update(const lgs_config_chg_
   char *saveptr = NULL;
   int rc = 0;
   bool logRecordDestinationConfiguration_list_clear = true;
-  bool logRecordDestinationStatus_list_clear = true;
 
   TRACE_ENTER();
   /* Validate config_data */
@@ -451,12 +456,6 @@ int lgs_cfg_update(const lgs_config_chg_
         logRecordDestinationConfiguration_list_clear = false;
       }
       lgs_conf.logRecordDestinationConfiguration.push_back(value_str);
-    } else if (strcmp(name_str, LOG_RECORD_DESTINATION_STATUS) == 0) {
-      if (logRecordDestinationStatus_list_clear) {
-        lgs_conf.logRecordDestinationStatus.clear();
-        logRecordDestinationStatus_list_clear = false;
-      }
-      lgs_conf.logRecordDestinationStatus.push_back(value_str);
     }
 
     param_ptr = next_ptr;
@@ -464,6 +463,9 @@ int lgs_cfg_update(const lgs_config_chg_
       break;
   }
 
+  // Configure destinations on standby node
+  CfgDestination(lgs_conf.logRecordDestinationConfiguration, ModifyType::kAdd);
+
   /* Config data is written. Mutex can be unlocked */
   osaf_mutex_unlock_ordie(&lgs_config_data_mutex);
 
@@ -693,6 +695,112 @@ static int lgs_cfg_verify_log_filesys_co
   return rc;
 }
 
+//>
+// Utility functions to validate destination configuration format
+//<
+
+// The format of destination must be one of followings:
+// 1) "name;type;"
+// 2) "name;type;value"
+// So, in destination configuration, must have 02 semiconlons
+// no more, no less.
+//
+bool is_right_destination_fmt(const VectorString& vdest) {
+  int nl_cnt = 0;
+  // Check each single destination
+  for (const auto& it : vdest) {
+    nl_cnt = std::count(it.begin(), it.end(), ';');
+    if (nl_cnt != 2) return false;
+  }
+  return true;
+}
+
+const char kSemicolon[] = ";";
+// Return false if "name" token is invalid
+bool is_name_valid(const VectorString& vdest) {
+  // Check each single destination
+  for (const auto& it : vdest) {
+    const VectorString sdes = logutil::Parser(it, kSemicolon);
+    if (sdes[kName].length() == 0) return false;
+    // invalid name if having special characters in
+    if (logutil::isUnixName(sdes[kName]) == false) return false;
+  }
+  return true;
+}
+
+// Return false if "type" token is invalid
+bool is_type_valid(const VectorString& vdest) {
+  // Check each single destination
+  for (const auto& it : vdest) {
+    const VectorString sdes = logutil::Parser(it, kSemicolon);
+    if (sdes[kType].length() == 0) return false;
+    // Only support type = "localsocket"
+    if (sdes[kType] != LocalSocketType::Type()) return false;
+  }
+  return true;
+}
+
+bool is_configuration_duplicated(const VectorString& vdest,
+                                 const VectorString& vdes2) {
+  // Compare values of each pair of destinations.
+  for (const auto& it : vdest) {
+    const VectorString sdes = logutil::Parser(it, kSemicolon);
+    for (const auto& it2 : vdes2) {
+      if (it == it2) continue;
+      const VectorString sdes2 = logutil::Parser(it2, kSemicolon);
+      // Duplicate name
+      if (sdes2[kName] == sdes[kName]) return false;
+      // Ignore nildest
+      if ((sdes[kValue].length() == 0) || (sdes2[kValue].length() == 0))
+        continue;
+      // Duplicate "value"
+      if (sdes2[kValue] == sdes[kValue]) return false;
+    }
+  }
+  return true;
+}
+
+// Check whether there is duplicated "name" or "value"
+// in provided @vdest and existing destinations
+bool check_configuration_duplicated(const VectorString& vdest,
+                                    ModifyType type) {
+  if (type == ModifyType::kDelete) return true;
+  // No checking duplicated if replacing to one value
+  if (type == ModifyType::kReplace && vdest.size() < 2) return true;
+  // Check in all replace values if any duplicated name/value
+  if (type == ModifyType::kReplace)  {
+    // Check whethere there is duplicated "name" or "value"
+    // in list of provided destination configurations.
+    return is_configuration_duplicated(vdest, vdest);
+  } else {
+    // Check whethere there is duplicated "name" or "value"
+    // in adding destination configurations and existing ones.
+    return is_configuration_duplicated(
+        vdest,
+        lgs_conf.logRecordDestinationConfiguration);
+  }
+}
+
+// Convert SaImmAttrModificationTypeT to ModifyType
+ModifyType lgs_convert_immtype(SaImmAttrModificationTypeT type) {
+  ModifyType mtype;
+
+  switch (type) {
+    case SA_IMM_ATTR_VALUES_ADD:
+      mtype = ModifyType::kAdd;
+      break;
+
+    case SA_IMM_ATTR_VALUES_DELETE:
+      mtype = ModifyType::kDelete;
+      break;
+
+    default:
+      mtype = ModifyType::kReplace;
+      break;
+  }
+
+  return mtype;
+}
 /**
  * Verify all values of log_record_destination_configuration
  * Rules:
@@ -700,26 +808,23 @@ static int lgs_cfg_verify_log_filesys_co
  * - String shall have at least three fields separated by '\n'
  * - First and second field cannot be empty
  *
- * @param log_record_destination_configuration[in]
+ * @param vdest[in] vector of destinations
  * @return -1 on error
  */
 int lgs_cfg_verify_log_record_destination_configuration(
-  std::vector<std::string>& log_record_destination_configuration) {
-  int rc = 0;
-  TRACE_ENTER();
-  
-  int nl_cnt = 0;
-  for (auto& config : log_record_destination_configuration) {
-    // Verify that the string contains at least 2 ';'
-    nl_cnt = std::count(config.begin(), config.end(), ';');
-    if (nl_cnt < 2) {
-      rc = -1;
-      break;
-    }
-  }
+    std::vector<std::string>& vdest,
+    SaImmAttrModificationTypeT type) {
+  ModifyType mtype = lgs_convert_immtype(type);
+  // Allow deleting all destinations.
+  if (vdest.size() == 0) return true;
+  // It is important to keep the check in order
+  bool result = (is_right_destination_fmt(vdest) &&
+                 is_name_valid(vdest) &&
+                 is_type_valid(vdest) &&
+                 check_configuration_duplicated(vdest, mtype)
+                 );
 
-  TRACE_LEAVE2("rc = %s", rc == -1? "Fail": "Pass");
-  return rc;
+  return (result == true) ? (0) : (-1);
 }
 
 
@@ -830,7 +935,8 @@ static int verify_all_init() {
   }
 
   if (lgs_cfg_verify_log_record_destination_configuration(
-      lgs_conf.logRecordDestinationConfiguration) == -1) {
+          lgs_conf.logRecordDestinationConfiguration,
+          SA_IMM_ATTR_VALUES_ADD) == -1) {
     lgs_conf.logRecordDestinationConfiguration.clear();
     lgs_conf.logRecordDestinationConfiguration_cnfflag = LGS_CNF_DEF;
   }
@@ -984,6 +1090,9 @@ static void read_logsv_config_obj_2() {
               lgs_conf.logRecordDestinationConfiguration.back().c_str());
       }
       lgs_conf.logRecordDestinationConfiguration_cnfflag = LGS_CNF_OBJ;
+      // Configure destinationstatus
+      CfgDestination(lgs_conf.logRecordDestinationConfiguration,
+                     ModifyType::kAdd);
     }
   }
 
@@ -1317,10 +1426,6 @@ const void *lgs_cfg_get(lgs_logconfGet_t
     case LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION:
       value_ptr = &lgs_conf.logRecordDestinationConfiguration;
       break;
-    case LGS_IMM_LOG_RECORD_DESTINATION_STATUS:
-      value_ptr = &lgs_conf.logRecordDestinationStatus;
-      break;
-
     case LGS_IMM_LOG_NUMBER_OF_PARAMS:
     case LGS_IMM_LOG_NUMEND:
     default:
@@ -1603,16 +1708,13 @@ static SaAisErrorT update_lgs_cfg_runtim
                                               SaImmAttrNameT attributeName,
                                               SaImmValueTypeT valueType) {
   TRACE_ENTER();
-  // Get the multi value stored as C++ vector
-  lgs_logconfGet_t attribute_id = param_name_to_id(attributeName);
-  const std::vector<std::string> *multi_value_list =
-      reinterpret_cast<const std::vector<std::string>*>
-      (lgs_cfg_get(attribute_id));
+
+  const VectorString multi_value_list = GetDestinationStatus();
 
   // Convert the multi value C++ vector to a void C array
   void **attrValues = nullptr;
   SaUint32T attrValuesNumber = vector_of_strings_to_attrValues(
-      multi_value_list, &attrValues);
+      &multi_value_list, &attrValues);
 
   // Give the multi value to IMM client
   SaAisErrorT ais_rc = update_runtime_attrValues(immOiHandle,
@@ -1807,7 +1909,7 @@ void lgs_trace_config() {
   TRACE("logFileSysConfig\t\t %u,\t %s",
         lgs_conf.logFileSysConfig,
         cnfflag_str(lgs_conf.logFileSysConfig_cnfflag));
-  
+
   // Multivalue:
   for (auto& conf_str : lgs_conf.logRecordDestinationConfiguration) {
     TRACE("logRecordDestinationConfiguration '%s', %s", conf_str.c_str(),
@@ -1816,13 +1918,6 @@ void lgs_trace_config() {
   if (lgs_conf.logRecordDestinationConfiguration.empty()) {
     TRACE("logRecordDestinationConfiguration <empty>");
   }
-  for (auto& conf_str : lgs_conf.logRecordDestinationStatus) {
-    TRACE("logRecordDestinationStatus '%s'", conf_str.c_str());
-  }
-  if (lgs_conf.logRecordDestinationStatus.empty()) {
-    TRACE("logRecordDestinationStatus <empty>");
-  }
-  
   TRACE("OpenSafLogConfig_object_exist\t %s",
         lgs_conf.OpenSafLogConfig_object_exist ? "True": "False");
   TRACE("===== LOG Configuration End =====");
@@ -1865,11 +1960,5 @@ void lgs_cfg_read_trace() {
   for (auto& conf_str : *dest_config) {
     TRACE("logRecordDestinationConfiguration '%s'", conf_str.c_str());
   }
-  const std::vector<std::string> *dest_status =
-      reinterpret_cast<const std::vector<std::string> *>
-      (lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_STATUS));
-  for (auto& conf_str : *dest_status) {
-    TRACE("logRecordDestinationStatus '%s'", conf_str.c_str());
-  }
   TRACE("##### LOG Configuration parameter read done  #####");
 }
diff --git a/src/log/logd/lgs_config.h b/src/log/logd/lgs_config.h
--- a/src/log/logd/lgs_config.h
+++ b/src/log/logd/lgs_config.h
@@ -302,7 +302,8 @@ int lgs_cfg_verify_mbox_limit(uint32_t h
 int lgs_cfg_verify_max_application_streams(uint32_t max_app_streams);
 int lgs_cfg_verify_file_io_timeout(uint32_t log_file_io_timeout);
 int lgs_cfg_verify_log_record_destination_configuration(
-                std::vector<std::string>& 
log_record_destination_configuration);
+    std::vector<std::string>& vdest,
+    SaImmAttrModificationTypeT type);
 /*
  * Functions for updating some parameters. Used to support check-point before
  * version 5
diff --git a/src/log/logd/lgs_dest.cc b/src/log/logd/lgs_dest.cc
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_dest.cc
@@ -0,0 +1,746 @@
+/*      -*- OpenSAF  -*-
+ *
+ * (C) Copyright 2017 The OpenSAF Foundation
+ * Copyright Ericsson AB 2017 - All Rights Reserved.
+ *
+ * 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. This file and program are licensed
+ * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Author(s): Ericsson AB
+ *
+ */
+
+#include "log/logd/lgs_dest.h"
+
+#include <algorithm>
+
+#include "log/saf/saLog.h"
+#include "base/logtrace.h"
+
+// RFC5424 syslog msg format
+// =========================
+// 1) HEADER
+// PRI             = 16*8 + SaLogSeverityT
+// VERSION         = 1
+// TIMESTAMP       = When a log record was actually logged
+// E.g: 2016-12-02T11:34:15.944364+01:00
+// HOSTNAME        = FQDN
+// APP-NAME        = App name who generated log record
+// PROCID          = NILVALUE
+// MSGID           = A hash generated from log stream DN
+// STRUCTURED-DATA = NILVALUE
+//
+// 2) MSG BODY
+// MSG             = The log record written to local file
+//
+
+static const char kDelimeter[] = ";";
+
+//==============================================================================
+// LocalSocketHandler class
+//==============================================================================
+LocalSocketHandler::LocalSocketHandler(const char* socket_name)
+    : base::UnixClientSocket{socket_name}
+    , sock_path_ {socket_name}
+    , status_{DestinationStatus::kInactive} {
+      Open();
+};
+
+void LocalSocketHandler::FlushStatus() {
+  if (fd() >= 0) {
+    status_ = DestinationStatus::kActive;
+  } else {
+    status_ = DestinationStatus::kFailed;
+  }
+}
+
+DestinationStatus LocalSocketHandler::GetSockStatus() {
+  Open();
+  return status_;
+}
+
+void LocalSocketHandler::Open() {
+  UnixClientSocket::Open();
+  FlushStatus();
+  // Do nothing
+}
+
+void LocalSocketHandler::FormRfc5424(
+    const DestinationHandler::RecordInfo& msg,
+    RfcBuffer* buf) {
+  base::LogMessage::Severity sev{logutil::Sev(msg.severity)};
+  base::LogMessage::HostName hostname{msg.origin};
+  base::LogMessage::ProcId procid{""};
+  base::LogMessage::AppName appname{msg.app_name};
+
+  base::LogMessage::Write(base::LogMessage::Facility::kLocal0,
+                          sev,
+                          msg.time,
+                          hostname,
+                          appname,
+                          procid,
+                          base::LogMessage::MsgId{msg.msgid},
+                          {},
+                          std::string{msg.log_record},
+                          buf);
+}
+
+ErrCode LocalSocketHandler::Send(const DestinationHandler::RecordInfo& msg) {
+  TRACE_ENTER();
+  RfcBuffer buffer;
+
+  FormRfc5424(msg, &buffer);
+
+  ssize_t length = buffer.size();
+  ssize_t len = UnixClientSocket::Send(buffer.data(), length);
+  // Resend as probably receiver has just been restarted.
+  // Retry only once, if not succesfully, leave it to upper layer decision.
+  if (len != length) {
+    LOG_NO("Failed to send log record to socket destination. Resent.");
+    len = UnixClientSocket::Send(buffer.data(), length);
+  }
+
+  FlushStatus();
+
+  TRACE_LEAVE();
+  return ((len != length) ? (ErrCode::kErr) : (ErrCode::kOk));
+}
+
+void LocalSocketHandler::Close() {
+  // Do nothing
+}
+
+LocalSocketHandler::~LocalSocketHandler() {
+  // Destination is deleted
+  status_ = DestinationStatus::kInactive;
+  // The parent class will do closing the connection
+  // and other resources.
+}
+
+//==============================================================================
+// LocalSocketType class
+//==============================================================================
+LocalSocketType LocalSocketType::me_;
+const char LocalSocketType::name_[] = "localsocket";
+
+LocalSocketType::LocalSocketType() {}
+
+LocalSocketType::~LocalSocketType() {
+  // Close all connections & stop the thread
+}
+
+const std::string LocalSocketType::DestinationStatus(const std::string& name) {
+  if (FindName(name) == false) return std::string{"inactive"};
+  const LocalSocketHandler* sk = nametype_map_[name];
+  if (sk == nullptr) return std::string{"inactive"};
+  return logutil::DestStatusToStr(nametype_map_[name]->GetSockStatus());
+}
+
+const VectorString LocalSocketType::GetAllDestStatus() {
+  VectorString output{};
+  if (me_.nametype_map_.size() == 0) return output;
+  for (const auto& it : me_.nametype_map_) {
+    std::string status = it.first + "," + me_.DestinationStatus(it.first);
+    output.push_back(status);
+  }
+  return output;
+}
+
+bool LocalSocketType::FindName(const std::string& name) const {
+  return ((nametype_map_.find(name) == nametype_map_.end()) ?
+          (false) : (true));
+}
+
+LocalSocketHandler* LocalSocketType::GetDestHandle(const std::string& name) {
+  if (FindName(name) == false) return nullptr;
+  return nametype_map_[name];
+}
+
+const char* LocalSocketType::GetCfgValue(const std::string& name) {
+  if (FindName(name) == false) return nullptr;
+  LocalSocketHandler* sk = nametype_map_[name];
+  return (sk == nullptr) ? nullptr : nametype_map_[name]->sock_path_.c_str();
+}
+
+ErrCode LocalSocketType::HandleRecordMsg(
+    const DestinationHandler::RecordMsg& msg) {
+  ErrCode ret = ErrCode::kOk;
+  // No destination configuration with this destination name
+  if (FindName(std::string{msg.name}) == false) {
+    LOG_WA("No such destination name(%s) configured", msg.name);
+    ret = ErrCode::kDrop;
+    return ret;
+  }
+
+  // NIL destination - only have "name" and "type", but no "value".
+  if (GetDestHandle(std::string{msg.name}) == nullptr) {
+    LOG_NO("The destination name(%s) not yet configured", msg.name);
+    ret = ErrCode::kDrop;
+    return ret;
+  }
+
+  // Send to destination end.
+  osafassert(nametype_map_[std::string{msg.name}] != nullptr);
+  return nametype_map_[std::string{msg.name}]->Send(msg.rec);
+}
+
+ErrCode LocalSocketType::HandleCfgMsg(
+    const DestinationHandler::CfgDestMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  const std::string name{msg.name};
+  const char* newcfg = msg.value;
+
+  // Request to create destination handler
+  if ((newcfg != nullptr) && (strlen(newcfg) > 0)) {
+    // Close and free previous @LocakSkDestHanlde instance if existing.
+    if (GetDestHandle(name) != nullptr) delete nametype_map_[name];
+    // Create a new destination handler @LocalSocketHandler for @name
+    nametype_map_[name] = new LocalSocketHandler(newcfg);
+    ret = (nametype_map_[name] == nullptr) ? ErrCode::kNoMem : ret;
+    return ret;
+  }
+
+  // Request to delete the existing destination handler (NILDEST case)
+  if (GetDestHandle(name) != nullptr) {
+    delete nametype_map_[name];
+    nametype_map_[name] = nullptr;
+  }
+
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode LocalSocketType::HandleDelCfgMsg(
+    const DestinationHandler::DelDestMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+
+  const std::string name{msg.name};
+  // Close and free @LocakSkDestHanlde instance if existing.
+  if (GetDestHandle(name) != nullptr) delete nametype_map_[name];
+  // Erase from the map
+  nametype_map_.erase(name);
+
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode LocalSocketType::ProcessMsg(
+    const DestinationHandler::HandleMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  static bool cfgsent = false;
+  // The proper sequence should be:
+  // 1) kCfgDest   : for configure destination handlers
+  // 2) kSendRecord: write log record to destination
+  // 3) kDelDest   : delete specific destinations
+  // 4) kNoDest    : delete all destinations
+  // Msg could be drop if the sequence is not correct.
+  switch (msg.type) {
+    case DestinationHandler::MsgType::kSendRecord: {
+      // No destination yet configured. Drop the message.
+      TRACE("%s %s", __func__, "RecordMsg msg");
+      const DestinationHandler::RecordMsg& rmsg = msg.info.rec;
+
+      // No destination has been configured so far.
+      if (cfgsent == false) {
+        LOG_WA("Destination handler not yet initialized. Drop msg.");
+        ret = ErrCode::kDrop;
+      } else {
+        ret = HandleRecordMsg(rmsg);
+      }
+      break;
+    }
+
+    case DestinationHandler::MsgType::kCfgDest: {
+      const DestinationHandler::CfgDestMsg& cmsg = msg.info.cfg;
+      TRACE("%s %s", __func__, "CfgDestMsg msg");
+      ret = HandleCfgMsg(cmsg);
+      // Notice there at leat one destination configured.
+      if (ret == ErrCode::kOk) cfgsent = true;
+      break;
+    }
+
+    case DestinationHandler::MsgType::kDelDest: {
+      const DestinationHandler::DelDestMsg& dmsg = msg.info.del;
+      TRACE("%s %s", __func__, "DelDestMsg msg");
+      // No destination yet configured. Drop the message.
+      if (cfgsent == false) {
+        LOG_NO("No destination configured yet. Drop msg.");
+        return ErrCode::kDrop;
+      }
+      ret = HandleDelCfgMsg(dmsg);
+      break;
+    }
+
+    case DestinationHandler::MsgType::kNoDest: {
+      LOG_NO("%s %s", __func__, "DelAllCfgDest msg");
+      if (cfgsent == false) {
+        LOG_NO("No destination configured yet");
+        return ErrCode::kDrop;
+      }
+
+      for (auto& it : nametype_map_) {
+        if (it.second != nullptr) delete it.second;
+        nametype_map_.erase(it.first);
+      }
+      cfgsent = false;
+      break;
+    }
+
+    default:
+      LOG_ER("Unknown dispatch message type %d", static_cast<int>(msg.type));
+      ret = ErrCode::kInvalid;
+  }
+
+  TRACE_LEAVE();
+  return ret;
+}
+
+//==============================================================================
+// DestinationHandler class
+//==============================================================================
+DestinationHandler DestinationHandler::me_;
+
+// Only support "kUnix" so far
+DestinationHandler::DestType DestinationHandler::GetDestType(
+    const std::string& type) {
+  if (type == LocalSocketType::Type()) {
+    return DestType::kUnix;
+  } else {
+    LOG_NO("Unknown destination type %s", type.c_str());
+    return DestType::kUnknown;
+  }
+}
+
+DestinationHandler::DestType DestinationHandler::ToDestType(
+    const std::string& name) {
+  if (nametype_map_.find(name) == nametype_map_.end())
+    return DestType::kUnknown;
+  return nametype_map_[name];
+}
+
+void DestinationHandler::UpdateDestTypeDb(
+    const DestinationHandler::HandleMsg& msg) {
+  MsgType mtype = msg.type;
+
+  switch (mtype) {
+    case MsgType::kCfgDest: {
+      TRACE("%s kCfgDest", __func__);
+      const CfgDestMsg& cdest = msg.info.cfg;
+      nametype_map_[std::string{cdest.name}] =
+          GetDestType(std::string{cdest.type});
+      break;
+    }
+
+    case MsgType::kDelDest: {
+      TRACE("%s kDelDest", __func__);
+      const DelDestMsg& deldest = msg.info.del;
+      nametype_map_.erase(std::string{deldest.name});
+      break;
+    }
+
+    case MsgType::kNoDest: {
+      TRACE("%s kNoDest", __func__);
+      for (auto& it : nametype_map_) nametype_map_.erase(it.first);
+      break;
+    }
+
+    default:
+      LOG_WA("Unknown Dispatch message type (%d)", static_cast<int>(mtype));
+      break;
+  }
+}
+
+ErrCode DestinationHandler::DispatchTo(
+    const DestinationHandler::HandleMsg& msg,
+    const std::string& name) {
+  ErrCode rc = ErrCode::kOk;
+  MsgType mtype = msg.type;
+  // All destinations has been deleted. Don't care of destination types.
+  // Delete all existing connections of all types.
+  if (mtype == MsgType::kNoDest) {
+    rc = LocalSocketType::Instance().ProcessMsg(msg);
+    // Update the destination names from @DestTypeDb
+    UpdateDestTypeDb(msg);
+    return rc;
+  }
+
+  // Update the @DestTypeDb first before processing the DestinationHandler msg.
+  // Otherwise, finding destination @type will result nothing.
+  // Then, the msg will be drop as no destination type found.
+  // But with @MsgType::kDelDest, updating should be last
+  // after fowarding the msg to @LocalSocketType.
+  // Otherwise, resource might be leaked (e.g: connection is not closed)
+  if (mtype == MsgType::kCfgDest) {
+    UpdateDestTypeDb(msg);
+  }
+
+  DestType destType = ToDestType(name);
+  switch (destType) {
+    // Pass the DestinationHandler msg to Local Unix Domain
+    // socket destination type for further processing.
+    case DestType::kUnix:
+      rc = LocalSocketType::Instance().ProcessMsg(msg);
+      break;
+
+    default:
+      LOG_ER("Not support this destination type(%d), or no dest configured!",
+             static_cast<int>(destType));
+      rc = ErrCode::kDrop;
+      break;
+  }
+
+  // Delete destination names from @DestTypeDb
+  if (mtype == MsgType::kDelDest) {
+    UpdateDestTypeDb(msg);
+  }
+
+  return rc;
+}
+
+void DestinationHandler::FormCfgDestMsg(
+    const std::string& dest, CfgDestMsg* msg) {
+  osafassert(msg != nullptr);
+  const VectorString tmp = logutil::Parser(dest, kDelimeter);
+  strncpy(msg->name, tmp[kName].c_str(), kMaxChar);
+  if (tmp.size() > 1) {
+    strncpy(msg->type, tmp[kType].c_str(), kMaxChar);
+  }
+  if (tmp.size() == kSize)
+    strncpy(msg->value, tmp[kValue].c_str(), kMaxChar);
+}
+
+void DestinationHandler::FormDelDestMsg(
+    const std::string& dest, DelDestMsg* msg) {
+  osafassert(msg != nullptr);
+  const VectorString tmp = logutil::Parser(dest, kDelimeter);
+  strncpy(msg->name, tmp[kName].c_str(), kMaxChar);
+}
+
+bool DestinationHandler::VectorFind(
+    const VectorString& vec, const std::string name) {
+  return std::find(vec.begin(), vec.end(), name) != vec.end();
+}
+
+ErrCode DestinationHandler::AddDestConfig(const VectorString& vdest) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  CfgDestMsg cmsg;
+  // Go thought all destinations and configure one by one
+  for (unsigned i = 0; i < vdest.size(); i++) {
+    HandleMsg msg{};
+    FormCfgDestMsg(vdest[i], &cmsg);
+    msg.type = MsgType::kCfgDest;
+    memcpy(&msg.info.cfg, &cmsg, sizeof(cmsg));
+    ret = DispatchTo(msg, cmsg.name);
+    allcfgdests_map_.push_back(vdest[i]);
+  }
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode DestinationHandler::DelDestConfig(const VectorString& vdest) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  DelDestMsg dmsg;
+  // Go thought all destinations and configure one by one
+  for (unsigned i = 0; i < vdest.size(); i++) {
+    if (VectorFind(allcfgdests_map_, vdest[i]) == false) {
+      LOG_NO("Not have such dest configuration (%s)", vdest[i].c_str());
+      ret = ErrCode::kDrop;
+      continue;
+    }
+
+    HandleMsg msg{};
+    FormDelDestMsg(vdest[i], &dmsg);
+    msg.type = MsgType::kDelDest;
+    memcpy(&msg.info.del, &dmsg, sizeof(dmsg));
+    ret = DispatchTo(msg, dmsg.name);
+    // Remove deleted destination from internal database
+    allcfgdests_map_.erase(std::remove(allcfgdests_map_.begin(),
+                                       allcfgdests_map_.end(), vdest[i]),
+                           allcfgdests_map_.end());
+  }
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode DestinationHandler::DelAllCfgDest() {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  // Don't care destination name
+  const std::string name{""};
+  HandleMsg msg{};
+  msg.type = MsgType::kNoDest;
+  ret = DispatchTo(msg, name);
+  allcfgdests_map_.clear();
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode DestinationHandler::ProcessCfgChange(
+    const VectorString& vdest, ModifyType type) {
+  ErrCode ret = ErrCode::kOk;
+  TRACE_ENTER();
+
+  // Case #1: perform all destination delete, but no existing at all.
+  if ((vdest.size() == 0) && (allcfgdests_map_.size() == 0)) {
+    return ErrCode::kOk;
+  }
+
+  // Case #2: Delete all existing destinations
+  if ((vdest.size() == 0) && (allcfgdests_map_.size() != 0)) {
+    return DelAllCfgDest();
+  }
+
+  // Case #3: Delete destination configurations while no dest cfg yet
+  if ((type == ModifyType::kDelete) &&
+      (vdest.size() != 0) &&
+      (allcfgdests_map_.size() == 0)) {
+    return ErrCode::kDrop;
+  }
+
+  // Case #4: Delete all destination configurations
+  if ((type == ModifyType::kDelete) &&
+      (vdest.size() == 0) &&
+      (allcfgdests_map_.size() != 0)) {
+    return DelAllCfgDest();
+  }
+
+  // Case #5: Delete all destinations but no one existing.
+  if ((type == ModifyType::kDelete) &&
+      (vdest.size() == 0) &&
+      (allcfgdests_map_.size() == 0)) {
+    return ErrCode::kDrop;
+  }
+
+  switch (type) {
+    case ModifyType::kAdd:
+      // Add new destinations to existing destinations.
+      ret = AddDestConfig(vdest);
+      break;
+
+    case ModifyType::kReplace:
+      // Step #1: Delete all existing destinations if any.
+      if (nametype_map_.size() != 0) {
+        DelAllCfgDest();
+      }
+      // Step #2: Create new destinations
+      ret = AddDestConfig(vdest);
+      break;
+
+    case ModifyType::kDelete:
+      ret = DelDestConfig(vdest);
+      break;
+
+    default:
+      LOG_ER("Unknown modify type (%d)", static_cast<int>(type));
+      ret = ErrCode::kErr;
+      break;
+  }
+
+  TRACE_LEAVE2("ret = %d", static_cast<int>(ret));
+  return ret;
+}
+
+ErrCode DestinationHandler::ProcessWriteReq(
+    const RecordInfo& info,
+    const std::string& name) {
+  HandleMsg msg{};
+  RecordMsg record;
+
+  msg.type = MsgType::kSendRecord;
+  strncpy(record.name, name.c_str(), kMaxChar);
+  memcpy(&record.rec, &info, sizeof(RecordInfo));
+  memcpy(&msg.info.rec, &record, sizeof(RecordMsg));
+  return DispatchTo(msg, name);
+}
+
+VectorString DestinationHandler::GetDestinationStatus() {
+  // For now, only support destination type "unix"
+  // If more than one destination type is support,
+  // should go though each, and append the result to output vector.
+  return LocalSocketType::Instance().GetAllDestStatus();
+}
+
+//==============================================================================
+// logutil namespace
+//==============================================================================
+namespace logutil {
+
+// Split a string @str with delimiter @delimiter
+// and return an vector of strings.
+VectorString Parser(const std::string& str,
+                    const std::string& delimiter) {
+  VectorString temp;
+  std::string s{str};
+  size_t pos = 0;
+  while ((pos = s.find(delimiter)) != std::string::npos) {
+    temp.push_back(s.substr(0, pos));
+    s.erase(0, pos + delimiter.length());
+  }
+  temp.push_back(s);
+  return temp;
+}
+
+std::string GenerateMsgId(const std::string& dn, bool isRtStream) {
+  const std::string parent = ",safApp=safLogService";
+  std::string msgid{""};
+  size_t posa = 0, posb = 0;
+
+  // Extract stream name from stream DN.
+  posa = dn.find('=');
+  if ((posb = dn.find(',')) == std::string::npos) {
+    posb = dn.length();
+  }
+  std::string sname = dn.substr(posa + 1, posb - posa - 1);
+
+  //>
+  // Rules to generate msgid string:
+  // 1) Use stream name (e.g: saLogSystem) + 'C'/'R' if it is not over 31 chars
+  //    and the stream DN contains the @parent ",safApp=safLogService";
+  //    Note: 'C' means configuration stream, 'R' means runtime stream.
+  // 2) Otherwise, generate a hash number from stream DN if stream name over 
31 chars.
+  //<
+  if (sname.length() < 32 && dn.find(parent) != std::string::npos) {
+    msgid = ((isRtStream == true) ? std::string{sname + 'R'} :
+             std::string{sname + 'C'});
+  } else {
+    msgid = std::to_string(std::hash<std::string>{}(dn));
+  }
+
+  return msgid;
+}
+
+// Convert AIS sev @a sev to Severity class
+base::LogMessage::Severity Sev(uint16_t sev) {
+  switch (sev) {
+    case SA_LOG_SEV_EMERGENCY:
+      return base::LogMessage::Severity::kEmerg;
+    case SA_LOG_SEV_ALERT:
+      return base::LogMessage::Severity::kAlert;
+    case SA_LOG_SEV_CRITICAL:
+      return base::LogMessage::Severity::kCrit;
+    case SA_LOG_SEV_ERROR:
+      return base::LogMessage::Severity::kErr;
+    case SA_LOG_SEV_WARNING:
+      return base::LogMessage::Severity::kWarning;
+    case SA_LOG_SEV_NOTICE:
+      return base::LogMessage::Severity::kNotice;
+    case SA_LOG_SEV_INFO:
+      return base::LogMessage::Severity::kInfo;
+    default:
+      LOG_ER("Unknown severity level (%d)", (int)sev);
+      return base::LogMessage::Severity::kErr;
+  }
+}
+
+bool isUnixName(const std::string& name) {
+  int index = 0;
+  char c;
+
+  while ((c = name[index++]) != '\0') {
+    switch (c) {
+      case '/':
+      case '|':
+      case ';':
+      case ',':
+      case '!':
+      case '@':
+      case '#':
+      case '$':
+      case '(':
+      case ')':
+      case '<':
+      case '>':
+      case '\\':
+      case '"':
+      case '\'':
+      case '`':
+      case '~':
+      case '{':
+      case '}':
+      case '[':
+      case ']':
+      case '+':
+      case '&':
+      case '^':
+      case '?':
+      case '*':
+      case '%':
+        return false;
+      default:
+        break;
+    }
+  }
+  return true;
+}
+
+const std::string DestStatusToStr(DestinationStatus status) {
+  switch (status) {
+    // Destination configured and is being connected to destination
+    case DestinationStatus::kActive: return "active";
+    // Destination configured and is being disconnected to destination
+    case DestinationStatus::kFailed: return "failed";
+    // Destination is not configured (nildest)
+    case DestinationStatus::kInactive: return "inactive";
+    default: return "unknown";
+  }
+}
+
+};  // namespace logutil
+
+//==============================================================================
+// Exposed interfaces to outer world
+//==============================================================================
+bool CfgDestination(const VectorString& vdest, ModifyType type) {
+  return (DestinationHandler::Instance().ProcessCfgChange(vdest, type) ==
+          ErrCode::kOk);
+}
+
+bool WriteToDestination(const RecordData& data,
+                        const VectorString& destnames) {
+  osafassert(data.name != nullptr && data.logrec != nullptr);
+  osafassert(data.hostname != nullptr && data.networkname != nullptr);
+
+  if (destnames.size() == 0) return true;
+
+  bool ret = true;
+  DestinationHandler::RecordInfo info;
+  std::string networkname = (std::string{data.networkname}.length() > 0) ?
+                            ("." + std::string {data.networkname}) : "";
+
+  // Origin is FQDN = <hostname>[.<networkname>]
+  const std::string origin = std::string {data.hostname} + networkname;
+  const std::string msgid = logutil::GenerateMsgId(data.name, data.isRtStream);
+
+  info.msgid      = msgid.c_str();
+  info.log_record = data.logrec;
+  info.record_id  = data.recordId;
+  info.stream_dn  = data.name;
+  info.app_name   = data.appname;
+  info.severity   = data.sev;
+  info.time       = data.time;
+  info.origin     = origin.c_str();
+
+  // Go thought all destination names and send to the destination
+  // that go with that destination name
+  for (const auto& destname : destnames) {
+    ret = (DestinationHandler::Instance().ProcessWriteReq(info, destname)
+           == ErrCode::kOk);
+  }
+
+  return ret;
+}
+
+VectorString GetDestinationStatus() {
+  return DestinationHandler::Instance().GetDestinationStatus();
+}
diff --git a/src/log/logd/lgs_dest.h b/src/log/logd/lgs_dest.h
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_dest.h
@@ -0,0 +1,580 @@
+/*      -*- OpenSAF  -*-
+ *
+ * (C) Copyright 2017 The OpenSAF Foundation
+ * Copyright Ericsson AB 2017 - All Rights Reserved.
+ *
+ * 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. This file and program are licensed
+ * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Author(s): Ericsson AB
+ *
+ */
+
+#ifndef SRC_LOG_LOGD_LGS_DEST_H_
+#define SRC_LOG_LOGD_LGS_DEST_H_
+
+#include <sys/time.h>
+#include <limits.h>
+#include <string>
+#include <map>
+#include <vector>
+#include <utility>
+#include <atomic>
+
+#include "base/log_message.h"
+#include "base/macros.h"
+#include "base/ncsgl_defs.h"
+#include "base/osaf_utility.h"
+#include "base/time.h"
+#include "base/unix_client_socket.h"
+
+// Status of connection to destination
+enum DestinationStatus {
+  // Destination is configured and is being connected to
+  kActive = 0,
+  // Destination is configured but not able to connected to
+  kFailed = 1,
+  // Configuration only have "name" and "type", but not have
+  // configuration value (NILDEST).
+  kInactive = 255
+};
+
+// This error code is introduced in alternative destination ticket #2258.
+// and for now, only be used internally in lgs_dest files.
+enum ErrCode {
+  // No error happens
+  kOk = 0,
+  // Error happens in general
+  kErr,
+  // Passing invalid parameters
+  kInvalid,
+  // Not able to serve the request as the requester is being busy
+  kBusy,
+  // The request is timeout
+  kTimeOut,
+  // No memory
+  kNoMem,
+  // The request/msg is drop as the precondition is not met
+  kDrop
+};
+
+// Type of modification on multiple attribute values.
+// Introduce this own enum as (xvunguy) don't want to
+// have dependent to IMM header file just because we need IMM modification 
type.
+enum ModifyType { kAdd = 1, kDelete = 2, kReplace = 3 };
+
+//>
+// Define the "token" possition in destination configuration.
+// Each destination configiration has format "name\ntype\nvalue".
+// After parsing, the output will put to the vector of strings
+// with format {"name", "type", "value"}.
+// kName : use this index to get "name"
+// kType : use this index to get "kType"
+// kValue: use this index to get "kValue"
+// kSize : this is the maximum size of the dest configuration vector.
+//<
+enum { kName = 0, kType, kValue, kSize };
+
+//==============================================================================
+// Two interfaces are published to outside world
+//==============================================================================
+
+// Typedef for shorten declaration
+// This type could be used to hold set of strings with formats
+// 1) {"name\ntype\nvalue", etc.}
+// 2) {"name", "type", "value"}
+// 3) etc.
+using VectorString = std::vector<std::string>;
+
+//>
+// @CfgDestination - Configure Destinations.
+// @input vdest: a vector of destination configurations.
+// @input type : modification type on destination configuration attribute.
+//               It has equivalent value to @SaImmAttrModificationTypeT.
+// @return: true if everything is OK, otherwise false.
+//
+// Call this function in CCB modified apply callback context,
+// when changes on configuration destination has been verified.
+// @type could be ModifyType::kAdd, ModifyType::kReplace, or 
ModifyType::kDelete
+//
+// E.g:
+// 1) Case #1: create 02 new destinations (@type = ModifyType::kAdd)
+//    Then, @vdest should contain data {"a\nb\nc", "d\ne\nf"}
+// 2) Case #2: modify one destinations. E.g: dest name @d -> @d1
+//    Then, @type = ModifyType::kReplace and @vdest contains data {"d1\ne\nf"}
+// 3) Case #3: delete one destination. Eg: dest name @a
+//    Then, @type = ModifyType::kDelete and @vdest should contains {"a\nb\nc"}
+// 4) Case #4: delete all destinations
+//    Then, @type = ModifyType::kDelete and @vdest contains empty vector {}
+//
+// The function will parse the provided data @vdest, getting
+// destination @name, destination @type, @destination value if any.
+// Then form the right handle msg, and forward it to right destination.
+//
+// This function is blocking. The caller will be blocked until
+// the request is done.
+//<
+bool CfgDestination(const VectorString& vdest, ModifyType type);
+
+//>
+// Carry necessary information to form RFC5424 syslog message.
+// Aim to reduce number of parameter passing to @WriteToDestination
+// @name       : log stream distinguish name
+// @logrec     : log record that written to local log file
+// @hostname   : log record originator
+// @networkname: network name
+// @recordId   : record id of @logrec
+// @sev        : severity of log record @logrec
+// @time       : Same timestamp as in the log record @logrec
+//<
+struct RecordData {
+  const char* name;
+  const char* logrec;
+  const char* hostname;
+  const char* networkname;
+  const char* appname;
+  bool isRtStream;
+  uint32_t recordId;
+  uint16_t sev;
+  timespec time;
+
+  RecordData() : name{nullptr}, logrec{nullptr}
+      , hostname{nullptr}
+      , networkname{nullptr}
+      , appname{nullptr}
+      , isRtStream{false}
+      , recordId{0}
+      , sev{0} {
+        time = base::ReadRealtimeClock();
+      }
+};
+
+//>
+// @WriteToDestination - Write @data To Destinations
+// @input:
+//   @data     : Carry necessary infos including log record.
+//   @destnames: Destination names wants to send log record to.
+// @return: true if successfully write to destinations. Oherwise, false.
+//
+// This function is blocking. The caller will be blocked until
+// the request is done.
+//<
+bool WriteToDestination(const RecordData& data,
+                        const VectorString& destnames);
+
+//>
+// @GetDestinationStatus - Get all destination status
+// @input : none
+// @output: vector of destination status with format
+//          {"name,status", "name1,status", etc.}
+//<
+VectorString GetDestinationStatus();
+
+namespace logutil {
+// Parse string format "a\nb\nc" to vector of string {a, b, c}
+VectorString Parser(const std::string&, const std::string&);
+// Extract the stream name from stream DN
+std::string GenerateMsgId(const std::string& dn, bool isRtStream);
+// Convert AIS log stream severity to syslog severity
+base::LogMessage::Severity Sev(uint16_t);
+// Check if @name follow unix naming rule
+bool isUnixName(const std::string& name);
+// Convert enum status to string
+const std::string DestStatusToStr(DestinationStatus status);
+};  // namespace logutil
+
+//>
+// @DestinationHandler class: Dispatch msg to right destination
+// All msgs, if want to come to destination handlers,
+// must go though the DestinationHandler via
+// the interface DestinationHandler::DispachTo()
+//
+// Three kind of DestinationHandler messages supported.
+// 1) Msg contains destination configurations such as
+//    destination name, destination type and info (local socket path).
+//
+// 2) Msg contains log record which is going to write to destination.
+//    Beside log record, msg may contain other info such as
+//    the stream name(DN) it writting to, log record severity,
+//    log record originator, log record id, or timestamp.
+//
+// 3) Msg contains destinations that is going to be deleted.
+//    This message may have list of deleted destination names.
+//
+// The proper order of DestinationHandler messages should be:
+// (1) -> (2) -> (3) or (1) -> (3).
+//
+// So, if msg contains log record comes(2) to the DestinationHandler first
+// somehow (this probably due to an coding hole, that does not
+// have a good check before sending msg to Dispacher) before
+// the configurating destination msg(1), the DestinationHandler will drop
+// the message, and log info to syslog.
+// The same to the case if deleting destination msg(3) comes first
+// before configuring destination(1), that msg(3) will be drop
+// and log info to syslog.
+//
+// According to information in that messages, the DestinationHandler
+// will do its jobs: interpret the msg, take the right destination
+// and transfer that the msg to the destination type for futher processing.
+//<
+class DestinationHandler {
+ public:
+  enum { kMaxChar = 255 };
+  enum class DestType { kUnix = 0, kUnknown };
+  //>
+  // Handle message types
+  // kSendRecord: for writing log record to destination(s).
+  // kCfgDest   : for configuring destination(s).
+  // kDelDest   : for deleting destination(s).
+  // kNoDest    : for deleting all destination(s).
+  //<
+  enum class MsgType { kSendRecord = 0, kCfgDest, kDelDest, kNoDest };
+
+  //>
+  // RecordMsg hold necessary information to forming RFC5424 protocol.
+  // @severity  : log severity of sent log record.
+  // @record_id : record id attached to sent log record.
+  // @hash      : hash number generated from log stream name (DN)
+  // @time      : time attached to log record. Using realtime if no set.
+  // @origin    : node originator of the log record (host & network name).
+  // @log_record: the log record that writting to local file.
+  // @stream_dn : log stream name(DN).
+  //<
+  struct RecordInfo {
+    const char* stream_dn;
+    const char* msgid;
+    const char* origin;
+    const char* log_record;
+    const char* app_name;
+    uint16_t severity;
+    uint32_t record_id;
+    struct timespec time;
+
+    // Set default value
+    RecordInfo() : stream_dn{nullptr}
+        , msgid{nullptr}
+        , origin{nullptr}
+        , log_record{nullptr}
+        , app_name{nullptr}
+        , severity{0}
+        , record_id{0}  {
+          time = base::ReadRealtimeClock();
+    }
+  };
+
+  //>
+  // @RecordMsg - send record @rec to destination name @name
+  // @name: destination name
+  // @rec : record message
+  //>
+  struct RecordMsg {
+    char name[kMaxChar];
+    RecordInfo rec;
+    RecordMsg() : name{0}, rec{} {}
+  };
+
+  //>
+  // CfgDestMsg - holds information for one destination.
+  // @name : name of destination.
+  // @type : type of destination.
+  // @value: destination configuration
+  //<
+  struct CfgDestMsg {
+    char name[kMaxChar];
+    char type[kMaxChar];
+    char value[kMaxChar];
+    // Clear the values of all types
+    CfgDestMsg() : name{0}, type{0}, value{0} {}
+  };
+
+  //>
+  // DelDestMsg - hold information for one destintaion name
+  // @name: refer to the destination name that is going to delete.
+  //<
+  struct DelDestMsg {
+    char name[kMaxChar];
+    DelDestMsg() : name{0} {}
+  };
+
+  //>
+  // @HandleMsg - Dispatch message format
+  // @type: the value depends on @info
+  // 1) kSendRecord if @info is RecordMsg
+  // 2) kCfgDest if @info is CfgDestMsg
+  // 3) kDelDest if @info is DelDestMsg
+  // 4) kNoDest if not above
+  //<
+  struct HandleMsg {
+    MsgType type;
+    union {
+      RecordMsg rec;
+      CfgDestMsg cfg;
+      DelDestMsg del;
+    } info;
+  };
+
+  // Unique object instance of @DestinationHandler class
+  static DestinationHandler& Instance() { return me_; }
+  // Do destination configuration basing on input @vdest and @type.
+  ErrCode ProcessCfgChange(const VectorString& vdest, ModifyType type);
+  // Do write log record @info to destinations @name
+  ErrCode ProcessWriteReq(const RecordInfo& info, const std::string& name);
+  // Get all destination status with format
+  // {"name,status", "name1,status1", etc.}
+  VectorString GetDestinationStatus();
+
+ private:
+  // typedef, aim for shorten declaration
+  using NameTypeMap = std::map<std::string, DestType>;
+  DestinationHandler() : nametype_map_{} {}
+  ~DestinationHandler() {}
+
+  // Dispatch the @HandleMsg msg to destination @name.
+  // Based on the destination @name, the method will find the right
+  // destination @type, then forward the message to for futher processing.
+  ErrCode DispatchTo(const HandleMsg& msg, const std::string& name);
+  // Pack @CfgDestMsg from the string @dest which have pure format
+  // "name\ntype\nvalue".
+  void FormCfgDestMsg(const std::string& dest, CfgDestMsg* msg);
+  // Pack @DelDestMsg basing on pure destination format
+  // "name\ntype\nvalue".
+  void FormDelDestMsg(const std::string& dest, DelDestMsg* msg);
+  // Form @CfgDestMsg and dispatch the msg.
+  ErrCode AddDestConfig(const VectorString& vdest);
+  // Form @DelDestMsg and dispatch the msg.
+  ErrCode DelDestConfig(const VectorString& vdest);
+  // Form @kNoDest dispatch msg
+  ErrCode DelAllCfgDest();
+  // Find name @name from @vec
+  bool VectorFind(const VectorString& vec, const std::string name);
+  // Return @DestType based on the destination string @type
+  DestType GetDestType(const std::string& type);
+  DestType ToDestType(const std::string& name);
+  // Update the @nametype_map_ whenever getting
+  // destination configuration change.
+  void UpdateDestTypeDb(const DestinationHandler::HandleMsg&);
+
+  // The map between destination @name and destination @type
+  NameTypeMap nametype_map_;
+  // Hold all existing destinations with pure format {"a\nb\nc", etc}
+  VectorString allcfgdests_map_;
+  static DestinationHandler me_;
+};
+
+//>
+// Represent connection to local Unix Domain socket
+// @LocalSocketHandler = Local Socket Destination class
+//
+// Each destination name will have its own instance
+// of this class. It represents the connection
+// to the destination. So, it contains connection status,
+// and the sequence message ID @msg_id_, showing how many logrecord
+// has been written to the socket. This sequence number
+// helps to detect if there is any msg lost so far.
+//
+// When getting a send log record request, this class
+// will form message complying with RFC5424 protocol, putting
+// the log record which has been writen to local file at MSG field,
+// also do fill information to other fields in protocol header.
+//
+// The instance of this class is only deleted if destination info (@value)
+// is deleted from Destination Configuration attribute.
+//
+// It would be wrong if more than one instance of this class
+// pointed to same Domain Unix Socket (same local path name).
+//
+// Regarding connection status, the status will be flushed to
+// latest one at points:
+// 1) Creating new destination:
+//    The instance will open a connection to destination,
+//    then update its status based on the file descriptor.
+//
+// 2) Change destination to new one:
+//    The previous connection is closed and opened a new one
+//    toward the destination, then update the status
+//    based on the file descriptor.
+//
+// 3) When writing log record to destination:
+//    This class is not aware of the destination whether
+//    the destination is dead or restarted.
+//    If it gets failed to write to socket, the status will be updated.
+//    just do that to detect the receiver on destination is dead/restarted.
+//
+// 4) Delete the destination
+//    When deleing destination, the instance will be deleted.
+//    Then, the status will be updated accordingly.
+//
+// All methods provided by this class should grant access
+// to @LocalSocketType only. No one else can directly
+// invoke any method of this class.
+//
+// With @Send(), if the msg get failed to send to destination,
+// will log info to syslog and return the error code to upper layer.
+//
+// The other information on this class is that, within @Send()
+// method, creating a very big stack buffer - over 65*1024 bytes
+// even the sent RFC msg is quite short (e.g: rfc header + 150 bytes).
+//<
+class LocalSocketHandler : public base::UnixClientSocket {
+ public:
+  // Set stack size of buffer to maximum size.
+  static const uint32_t kBufMaxSize = 65*1024 + 1024;
+  // typedef, aim to shorten declaration.
+  using RfcBuffer = base::Buffer<kBufMaxSize>;
+
+  // This class is only dedicated to LocalSocketType.
+  friend class LocalSocketType;
+  // Open and connect to the socket
+  void Open();
+  // @Close Do nothing
+  void Close();
+  // Form RFC5424 and send to the socket
+  ErrCode Send(const DestinationHandler::RecordInfo&);
+  // Get the socket status
+  DestinationStatus GetSockStatus();
+  // Form rfc5424 syslog format
+  static void FormRfc5424(
+      const DestinationHandler::RecordInfo& msg,
+      RfcBuffer* buf);
+
+ private:
+  explicit LocalSocketHandler(const char*);
+  ~LocalSocketHandler();
+
+  // Get the @status_ up-to-date
+  void FlushStatus();
+
+  // Hold destination info (@value)
+  std::string sock_path_;
+  // Hold the connection status
+  DestinationStatus status_;
+
+  DELETE_COPY_AND_MOVE_OPERATORS(LocalSocketHandler);
+};
+
+//>
+// @LocalSocketType: Local Socket Destination Type
+//
+// This class represents local socket destination type.
+// Every destinations name with local socket destination type
+// must refer to one and only one instance of this class.
+// Therefore, it has been designed as a singleton pattern.
+//
+// Outside world are not allowed to see any methods of this class
+// except DestinationHandler. Right after analyzing DestinationHandler 
messages,
+// knowing what is the destination type, DestinationHandler will pass
+// the message to this class via the unique instance.
+//
+// This class holds a map b/w destination name and @LocalSocketHandler instance
+// that represents the connection to destination for that destination name.
+//
+// Based on what type of messages come to, it will take specific actions
+// accordingly.
+// 1) Create/Update destination message
+//    The message will contain destination name, destination type,
+//    and probably destination info (local socket path).
+//    Basing on the destination name, the Handler will sure that
+//    a) Any such destination name has been created before?
+//    b) And any @LocalSocketHandler instance is mapped to that
+//       destination name?
+//    If no destination name exist in the @map, then Hander will
+//    create a new pair <name, @LocalSocketHandler> and update its destination.
+//    If the destination name already exist, will check if configuration info
+//    provided in the message is different with the existing or not.
+//    We expect it should be different with the existing, otherwise
+//    we have code fault somewhere. If different from the existing, closing
+//    the old connection, and creating new one and re-map to that name.
+//
+// 2) Sending log record message
+//    The message will contain destination name, log record, log stream DN
+//    and other infos. Basing on the destination name, the Handler will sure:
+//    a) Any such destination name has been created before?
+//    b) And any @LocalSocketHandler instance is mapped to that
+//       destination name?
+//    With this message, the Handler expects the destination name and
+//    @LocalSocketHandler must exist. Otherwise, there is a code
+//    fault somewhere.
+//    and the Handler will drop that message and log info to syslog.
+//    If as expected, the Handler will send the message to
+//    @LocalSocketHandler instance, in turn,
+//    it will form that message according to RF5424 format and
+//    send that message to local unix socket.
+//
+// 3) Delete destination message
+//    This message will contain only destination name. Basing on that info,
+//    the Hanlder will find that name in the @map to see any name is there.
+//    If have, do:
+//    1) Close the connection
+//    2) Free resources connected to that destination name
+//    Once destination name is deleted, any log record sent to destination name
+//    will be drop or no writing to the destination happens.
+//<
+class LocalSocketType {
+ public:
+  // @LocalSocketType class is dedicated to @DestinationHandler class.
+  // Outside world is not granted to access to this class resources
+  friend class DestinationHandler;
+
+  // Get @type that represents for @LocalSocketType. This type must be unique.
+  static const std::string Type() { return std::string{name_}; }
+
+ private:
+  // typedef for the map. Aim to shorten the type declaration.
+  using NameTypeMap =  std::map<std::string, LocalSocketHandler*>;
+
+  LocalSocketType();
+  ~LocalSocketType();
+
+  // Get @LocalSocketHandler instance which has been assigned
+  // to this destination @name
+  LocalSocketHandler* GetDestHandle(const std::string& name);
+  // Get configuration value that goes with this destination name @name
+  const char* GetCfgValue(const std::string& name);
+  // Find if any destination @name configured so far
+  bool FindName(const std::string&) const;
+  // Interpret the DestinationHandler msg and forward to right handle
+  ErrCode ProcessMsg(const DestinationHandler::HandleMsg&);
+  // Processing @Dispacher::RecordMsg msg, then forward to its instance
+  // @LocalSocketHandler to form RFC5424 syslog format before writing to 
socket.
+  ErrCode HandleRecordMsg(const DestinationHandler::RecordMsg&);
+  // Processing @DestinationHandler::CfgDestMsg msg. Go thought all 
destinations
+  // and create or update destination configuration depending on contents.
+  ErrCode HandleCfgMsg(const DestinationHandler::CfgDestMsg&);
+  // Processing @DestinationHandler::CfgDestMsg msg. Go thought all deleted
+  // destinations names, then do close connections and free resources.
+  ErrCode HandleDelCfgMsg(const DestinationHandler::DelDestMsg&);
+  // Get destination status for destination @name.
+  // The value could be:
+  // 1) "active": destination @name is configured and connected to other end.
+  // 2) "failed": destination @name is configured, but not able co connect to.
+  // 3) "inactive": no destination configuration value that goes with @name
+  const std::string DestinationStatus(const std::string& name);
+  // Get the unique instance represents this unique type
+  // This method should only be used by @DestinationHandler class
+  // Get all destination status for Local Socket Destination Typen
+  // @return a vector of status, with format
+  // {"name,active", "name2,inactive, etc."}
+  static const VectorString GetAllDestStatus();
+  // Unique object instance for this @LocalSocketType class
+  static LocalSocketType& Instance() { return me_; }
+  // Unique instance for this class
+  static LocalSocketType me_;
+  // The type name of this @LocalSocketType (my identity).
+  // Must use this "type" name in destination configuration
+  // if want to destinate to local unix socket.
+  static const char name_[];
+
+  // Map b/w dest name and its own @LocalSocketHandler instance.
+  // Same @name must have same @LocalSocketHandler instance.
+  NameTypeMap nametype_map_;
+
+  DELETE_COPY_AND_MOVE_OPERATORS(LocalSocketType);
+};
+
+#endif  // SRC_LOG_LOGD_LGS_DEST_H_
diff --git a/src/log/logd/lgs_evt.cc b/src/log/logd/lgs_evt.cc
--- a/src/log/logd/lgs_evt.cc
+++ b/src/log/logd/lgs_evt.cc
@@ -1,6 +1,7 @@
 /*      -*- OpenSAF  -*-
  *
  * (C) Copyright 2008 The OpenSAF Foundation
+ * Copyright Ericsson AB 2014, 2017 - All Rights Reserved.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
@@ -27,6 +28,7 @@
 #include "lgs_imm_gcfg.h"
 #include "base/osaf_extended_name.h"
 #include "lgs_clm.h"
+#include "lgs_dest.h"
 
 /* Macro to validate the version */
 #define m_LOG_VER_IS_VALID(ver)                                         \
@@ -1235,6 +1237,8 @@ static uint32_t proc_write_log_async_msg
   void *ckpt_ptr;
   uint32_t max_logrecsize = 0;
   char node_name[_POSIX_HOST_NAME_MAX];
+  RecordData data;
+  timespec time;
 
   memset(node_name, 0, _POSIX_HOST_NAME_MAX);
   strncpy(node_name, evt->node_name, _POSIX_HOST_NAME_MAX);
@@ -1294,6 +1298,35 @@ static uint32_t proc_write_log_async_msg
     goto done;
   }
 
+  //>
+  // Has successfully written log record to file.
+  // Now, send to destination if any destination name set.
+  //<
+
+  // Streaming not support on alarm/notif streams.
+  if ((stream->name == SA_LOG_STREAM_ALARM) ||
+      (stream->name == SA_LOG_STREAM_NOTIFICATION)) {
+    goto checkpoint;
+  }
+
+  // Packing Record data that carry necessary information
+  // to form RFC5424 syslog msg, then send to destination name(s).
+  data.name = stream->name.c_str();
+  data.logrec = logOutputString;
+  data.hostname = node_name;
+  data.networkname = lgs_get_networkname().c_str();
+  data.appname = osaf_extended_name_borrow(
+      param->logRecord->logHeader.genericHdr.logSvcUsrName);
+  data.isRtStream = stream->isRtStream;
+  data.recordId = stream->logRecordId;
+  data.sev = param->logRecord->logHeader.genericHdr.logSeverity;
+  time.tv_sec = (param->logRecord->logTimeStamp / (SaTimeT)SA_TIME_ONE_SECOND);
+  time.tv_nsec = (param->logRecord->logTimeStamp % 
(SaTimeT)SA_TIME_ONE_SECOND);
+  data.time = time;
+
+  WriteToDestination(data, stream->dest_names);
+
+checkpoint:
   /* TODO: send fail back if ack is wanted, Fix counter for application 
stream!! */
   if (cb->ha_state == SA_AMF_HA_ACTIVE) {
     if (lgs_is_peer_v2()) {
diff --git a/src/log/logd/lgs_imm.cc b/src/log/logd/lgs_imm.cc
--- a/src/log/logd/lgs_imm.cc
+++ b/src/log/logd/lgs_imm.cc
@@ -45,12 +45,14 @@
 #include "log/logd/lgs_file.h"
 #include "log/logd/lgs_recov.h"
 #include "log/logd/lgs_config.h"
+#include "log/logd/lgs_dest.h"
 #include "base/saf_error.h"
 
 #include "lgs_mbcsv_v1.h"
 #include "lgs_mbcsv_v2.h"
 #include "lgs_mbcsv_v3.h"
 #include "lgs_mbcsv_v5.h"
+#include "lgs_mbcsv_v7.h"
 
 /* TYPE DEFINITIONS
  * ----------------
@@ -234,11 +236,33 @@ static uint32_t ckpt_stream_config(log_s
   uint32_t rc;
   lgsv_ckpt_msg_v1_t ckpt_v1;
   lgsv_ckpt_msg_v2_t ckpt_v2;
+  lgsv_ckpt_msg_v7_t ckpt_v3;
+
   void *ckpt_ptr;
 
   TRACE_ENTER();
 
-  if (lgs_is_peer_v2()) {
+  if (lgs_is_peer_v7()) {
+    memset(&ckpt_v3, 0, sizeof(ckpt_v3));
+    ckpt_v3.header.ckpt_rec_type = LGS_CKPT_CFG_STREAM;
+    ckpt_v3.header.num_ckpt_records = 1;
+    ckpt_v3.header.data_len = 1;
+
+    ckpt_v3.ckpt_rec.stream_cfg.name = const_cast<char 
*>(stream->name.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.fileName = const_cast<char 
*>(stream->fileName.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.pathName = const_cast<char 
*>(stream->pathName.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.maxLogFileSize = stream->maxLogFileSize;
+    ckpt_v3.ckpt_rec.stream_cfg.fixedLogRecordSize = 
stream->fixedLogRecordSize;
+    ckpt_v3.ckpt_rec.stream_cfg.logFullAction = stream->logFullAction;
+    ckpt_v3.ckpt_rec.stream_cfg.logFullHaltThreshold = 
stream->logFullHaltThreshold;
+    ckpt_v3.ckpt_rec.stream_cfg.maxFilesRotated = stream->maxFilesRotated;
+    ckpt_v3.ckpt_rec.stream_cfg.logFileFormat = stream->logFileFormat;
+    ckpt_v3.ckpt_rec.stream_cfg.severityFilter = stream->severityFilter;
+    ckpt_v3.ckpt_rec.stream_cfg.logFileCurrent = const_cast<char 
*>(stream->logFileCurrent.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.dest_names = const_cast<char 
*>(stream->stb_dest_names.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.c_file_close_time_stamp = 
stream->act_last_close_timestamp;
+    ckpt_ptr = &ckpt_v3;
+  } else if (lgs_is_peer_v2()) {
     memset(&ckpt_v2, 0, sizeof(ckpt_v2));
     ckpt_v2.header.ckpt_rec_type = LGS_CKPT_CFG_STREAM;
     ckpt_v2.header.num_ckpt_records = 1;
@@ -860,7 +884,8 @@ static SaAisErrorT config_ccb_completed_
         char *value_str = *(reinterpret_cast<char **>(value));
         values_vector.push_back(value_str);
       }
-      rc = lgs_cfg_verify_log_record_destination_configuration(values_vector);
+      rc = lgs_cfg_verify_log_record_destination_configuration(
+          values_vector, attrMod->modType);
       if (rc == -1) {
         report_oi_error(immOiHandle, opdata->ccbId,
                         "%s value is NOT accepted", attribute->attrName);
@@ -1268,6 +1293,20 @@ done:
   return rc;
 }
 
+static bool is_valid_dest_names(const std::vector<std::string>& names) {
+  // Delete all destination names
+  if (names.size() == 0) return true;
+  for (const auto& it : names) {
+    // Empty destination name is invalid
+    if (it.empty() == true || it.size() == 0) return false;
+    // Contain special characters is not allowed
+    if (logutil::isUnixName(it) == false) return false;
+    // Name is too long
+    if (it.length() > 255) return false;
+  }
+  return true;
+}
+
 /**
  * Validate input parameters creation and modify of a persistent stream
  * i.e. a stream that has a configuration object.
@@ -1366,12 +1405,16 @@ static SaAisErrorT check_attr_validity(S
     if (attribute->attrValuesNumber > 0) {
       value = attribute->attrValues[0];
     } else if (opdata->operationType == CCBUTIL_MODIFY) {
-      /* An attribute without a value is never valid if modify */
-      report_oi_error(immOiHandle, opdata->ccbId,
-                      "Attribute %s has no value",attribute->attrName);
-      TRACE("Modify: Attribute %s has no value",attribute->attrName);
-      rc = SA_AIS_ERR_BAD_OPERATION;
-      goto done;
+      if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+        // do nothing
+      } else {
+        /* An attribute without a value is never valid if modify */
+        report_oi_error(immOiHandle, opdata->ccbId,
+                        "Attribute %s has no value",attribute->attrName);
+        TRACE("Modify: Attribute %s has no value",attribute->attrName);
+        rc = SA_AIS_ERR_BAD_OPERATION;
+        goto done;
+      }
     } else {
       /* If create all attributes will be present also the ones without
        * any value.
@@ -1426,8 +1469,24 @@ static SaAisErrorT check_attr_validity(S
       i_severityFilter_mod = true;
       TRACE("Saved attribute \"%s\" = %d", attribute->attrName,
             i_severityFilter);
+    } else if (!strcmp(attribute->attrName,"saLogRecordDestination")) {
+      std::vector<std::string> vstring{};
+      for (unsigned i = 0; i < attribute->attrValuesNumber; i++) {
+        value = attribute->attrValues[i];
+        char *value_str = *(reinterpret_cast<char **>(value));
+        vstring.push_back(value_str);
+      }
+      if (is_valid_dest_names(vstring) == false) {
+        /* Report failed if has special character in file name */
+        rc = SA_AIS_ERR_BAD_OPERATION;
+        report_oi_error(immOiHandle, opdata->ccbId,
+                        "Invalid saLogRecordDestination value");
+        TRACE("Invalid saLogRecordDestination value");
+        goto done;
+      }
     }
 
+
     /* Get next attribute or detect no more attributes */
  next:
     if (opdata->operationType == CCBUTIL_CREATE) {
@@ -2250,6 +2309,15 @@ static SaAisErrorT stream_create_and_con
       } else if (!strcmp(ccb->param.create.attrValues[i]->attrName, 
"saLogStreamSeverityFilter")) {
         (*stream)->severityFilter = *((SaUint32T *) value);
         TRACE("severityFilter: %u", (*stream)->severityFilter);
+      } else if (!strcmp(ccb->param.create.attrValues[i]->attrName,
+                          "saLogRecordDestination")) {
+        std::vector<std::string> vstring{};
+        for (unsigned ii = 0; ii < 
ccb->param.create.attrValues[i]->attrValuesNumber; ii++) {
+          value = ccb->param.create.attrValues[i]->attrValues[ii];
+          char *value_str = *(reinterpret_cast<char **>(value));
+          vstring.push_back(value_str);
+        }
+        log_stream_add_dest_name(*stream, vstring);
       }
     }
     i++;
@@ -2304,6 +2372,30 @@ static void stream_ccb_apply_create(cons
   TRACE_LEAVE();
 }
 
+static void apply_destination_changes(
+    log_stream_t* stream,
+    const std::vector<std::string>& destname,
+    SaImmAttrModificationTypeT type) {
+  switch (type) {
+    case SA_IMM_ATTR_VALUES_ADD:
+      log_stream_add_dest_name(stream, destname);
+      break;
+
+    case SA_IMM_ATTR_VALUES_DELETE:
+      log_stream_delete_dest_name(stream, destname);
+      break;
+
+    case SA_IMM_ATTR_VALUES_REPLACE:
+      log_stream_replace_dest_name(stream, destname);
+      break;
+
+    default:
+      // Shall never happen
+      LOG_ER("%s: Unknown modType %d", __FUNCTION__, type);
+      osafassert(0);
+  }
+}
+
 static void stream_ccb_apply_modify(const CcbUtilOperationData_t *opdata) {
   const SaImmAttrModificationT_2 *attrMod;
   int i = 0;
@@ -2323,12 +2415,21 @@ static void stream_ccb_apply_modify(cons
 
   attrMod = opdata->param.modify.attrMods[i++];
   while (attrMod != NULL) {
-    void *value;
+    void *value = nullptr;
     const SaImmAttrValuesT_2 *attribute = &attrMod->modAttr;
 
     TRACE("attribute %s", attribute->attrName);
-
-    value = attribute->attrValues[0];
+    if (attribute->attrValuesNumber != 0) {
+      value = attribute->attrValues[0];
+    } else {
+      if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+        const std::vector<std::string> dname{};
+        LOG_NO("%s deleted", __func__);
+        log_stream_delete_dest_name(stream, dname);
+        attrMod = opdata->param.modify.attrMods[i++];
+        continue;
+      }
+    }
 
     if (!strcmp(attribute->attrName, "saLogStreamFileName")) {
       fileName = *((char **)value);
@@ -2362,6 +2463,14 @@ static void stream_ccb_apply_modify(cons
     } else if (!strcmp(attribute->attrName, "saLogStreamSeverityFilter")) {
       SaUint32T severityFilter = *((SaUint32T *)value);
       stream->severityFilter = severityFilter;
+    } else if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+      std::vector<std::string> vstring;
+      for (unsigned i = 0; i < attribute->attrValuesNumber; i++) {
+        value = attribute->attrValues[i];
+        char *value_str = *(reinterpret_cast<char **>(value));
+        vstring.push_back(value_str);
+      }
+      apply_destination_changes(stream, vstring, attrMod->modType);
     } else {
       LOG_ER("Error: Unknown attribute name");
       osafassert(0);
@@ -2613,6 +2722,7 @@ static SaAisErrorT stream_create_and_con
     const_cast<char *>("saLogStreamLogFullHaltThreshold"),
     const_cast<char *>("saLogStreamMaxFilesRotated"),
     const_cast<char *>("saLogStreamLogFileFormat"),
+    const_cast<char *>("saLogRecordDestination"),
     const_cast<char *>("saLogStreamSeverityFilter"),
     const_cast<char *>("saLogStreamCreationTimestamp"),
     NULL
@@ -2696,6 +2806,14 @@ static SaAisErrorT stream_create_and_con
     } else if (!strcmp(attribute->attrName, "saLogStreamSeverityFilter")) {
       stream->severityFilter = *((SaUint32T *)value);
       TRACE("severityFilter: %u", stream->severityFilter);
+    } else if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+      std::vector<std::string> vstring{};
+      for (unsigned i = 0; i < attribute->attrValuesNumber; i++) {
+        value = attribute->attrValues[i];
+        char *value_str = *(reinterpret_cast<char **>(value));
+        vstring.push_back(value_str);
+      }
+      log_stream_add_dest_name(stream, vstring);
     } else if (!strcmp(attribute->attrName, "saLogStreamCreationTimestamp")) {
       if (attribute->attrValuesNumber != 0) {
         /* Restore creation timestamp if exist
diff --git a/src/log/logd/lgs_mbcsv.cc b/src/log/logd/lgs_mbcsv.cc
--- a/src/log/logd/lgs_mbcsv.cc
+++ b/src/log/logd/lgs_mbcsv.cc
@@ -1,6 +1,7 @@
 /*      -*- OpenSAF  -*-
  *
  * (C) Copyright 2008 The OpenSAF Foundation
+ * Copyright Ericsson AB 2014, 2017 - All Rights Reserved.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
@@ -20,11 +21,14 @@
 #include "base/ncssysf_mem.h"
 #include "base/osaf_time.h"
 
+#include "log/logd/lgs_mbcsv_v7.h"
 #include "lgs_mbcsv_v5.h"
 #include "lgs_mbcsv_v3.h"
 #include "lgs_mbcsv_v2.h"
 #include "lgs_mbcsv_v1.h"
 #include "lgs_recov.h"
+#include "osaf/immutil/immutil.h"
+#include "log/logd/lgs_dest.h"
 
 /*
   LGS_CKPT_DATA_HEADER
@@ -360,6 +364,18 @@ bool lgs_is_peer_v5() {
 }
 
 /**
+ * Check if peer is version 7 (or later)
+ * @return bool
+ */
+bool lgs_is_peer_v7() {
+  if (lgs_cb->mbcsv_peer_version >= LGS_MBCSV_VERSION_7) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+/**
  * Check if configured for split file system.
  * If other node is version 1 split file system mode is not applicable.
  *
@@ -750,6 +766,7 @@ static uint32_t edu_enc_reg_list(lgs_cb_
  ****************************************************************************/
 
 static uint32_t ckpt_encode_async_update(lgs_cb_t *lgs_cb, EDU_HDL edu_hdl, 
NCS_MBCSV_CB_ARG *cbk_arg) {
+  lgsv_ckpt_msg_v7_t *data_v7 = NULL;
   lgsv_ckpt_msg_v5_t *data_v5 = NULL;
   lgsv_ckpt_msg_v3_t *data_v3 = NULL;
   lgsv_ckpt_msg_v2_t *data_v2 = NULL;
@@ -761,7 +778,12 @@ static uint32_t ckpt_encode_async_update
 
   TRACE_ENTER();
   /* Set reo_hdl from callback arg to ckpt_rec */
-  if (lgs_is_peer_v5()) {
+  if (lgs_is_peer_v7()) {
+    data_v7 = reinterpret_cast<lgsv_ckpt_msg_v7_t *>(
+        static_cast<long>(cbk_arg->info.encode.io_reo_hdl));
+    vdata = data_v7;
+    edp_function = edp_ed_ckpt_msg_v7;
+  } else if (lgs_is_peer_v5()) {
     data_v5 = reinterpret_cast<lgsv_ckpt_msg_v5_t *>(
         static_cast<long>(cbk_arg->info.encode.io_reo_hdl));
     vdata = data_v5;
@@ -1047,7 +1069,11 @@ static uint32_t ckpt_decode_log_cfg_stre
   void *stream_cfg;
   EDU_PROG_HANDLER edp_function;
 
-  if (lgs_is_peer_v2()) {
+  if (lgs_is_peer_v7()) {
+    lgsv_ckpt_msg_v7_t *ckpt_msg_v7 = static_cast<lgsv_ckpt_msg_v7_t 
*>(ckpt_msg);
+    stream_cfg = &ckpt_msg_v7->ckpt_rec.stream_cfg;
+    edp_function = edp_ed_cfg_stream_rec_v7;
+  } else if (lgs_is_peer_v2()) {
     lgsv_ckpt_msg_v2_t *ckpt_msg_v2 = static_cast<lgsv_ckpt_msg_v2_t 
*>(ckpt_msg);
     stream_cfg = &ckpt_msg_v2->ckpt_rec.stream_cfg;
     edp_function = edp_ed_cfg_stream_rec_v2;
@@ -1111,6 +1137,8 @@ static uint32_t ckpt_decode_async_update
   lgsv_ckpt_msg_v3_t *ckpt_msg_v3 = &msg_v3;
   lgsv_ckpt_msg_v5_t msg_v5;
   lgsv_ckpt_msg_v5_t *ckpt_msg_v5 = &msg_v5;
+  lgsv_ckpt_msg_v7_t msg_v7;
+  lgsv_ckpt_msg_v7_t *ckpt_msg_v7 = &msg_v7;
   void *ckpt_msg;
   lgsv_ckpt_header_t hdr, *hdr_ptr = &hdr;
 
@@ -1130,7 +1158,10 @@ static uint32_t ckpt_decode_async_update
 
   TRACE_2("\tckpt_rec_type: %d ", (int)hdr_ptr->ckpt_rec_type);
 
-  if (lgs_is_peer_v5()) {
+  if (lgs_is_peer_v7()) {
+    ckpt_msg_v7->header = hdr;
+    ckpt_msg = ckpt_msg_v7;
+  } else if (lgs_is_peer_v5()) {
     ckpt_msg_v5->header = hdr;
     ckpt_msg = ckpt_msg_v5;
   } else if (lgs_is_peer_v4()) {
@@ -1148,7 +1179,9 @@ static uint32_t ckpt_decode_async_update
   switch (hdr_ptr->ckpt_rec_type) {
     case LGS_CKPT_CLIENT_INITIALIZE:
       TRACE_2("\tINITIALIZE REC: UPDATE");
-      if (lgs_is_peer_v5()) {
+      if (lgs_is_peer_v7()) {
+        reg_rec = &ckpt_msg_v7->ckpt_rec.initialize_client;
+      } else if (lgs_is_peer_v5()) {
         reg_rec = &ckpt_msg_v5->ckpt_rec.initialize_client;
       } else if (lgs_is_peer_v4()) {
         reg_rec = &ckpt_msg_v3->ckpt_rec.initialize_client;
@@ -1173,7 +1206,9 @@ static uint32_t ckpt_decode_async_update
 
     case LGS_CKPT_OPEN_STREAM: /* 4 */
       TRACE_2("\tSTREAM OPEN: UPDATE");
-      if (lgs_is_peer_v5()) {
+      if (lgs_is_peer_v7()) {
+        stream_open = &ckpt_msg_v7->ckpt_rec.stream_open;
+      } else if (lgs_is_peer_v5()) {
         stream_open = &ckpt_msg_v5->ckpt_rec.stream_open;
       } else if (lgs_is_peer_v4()) {
         stream_open = &ckpt_msg_v3->ckpt_rec.stream_open;
@@ -1417,13 +1452,17 @@ static uint32_t process_ckpt_data(lgs_cb
   lgsv_ckpt_msg_v2_t *data_v2;
   lgsv_ckpt_msg_v3_t *data_v3;
   lgsv_ckpt_msg_v5_t *data_v5;
+  lgsv_ckpt_msg_v7_t *data_v7;
 
   if ((!cb) || (data == NULL)) {
     TRACE("%s - FAILED: (!cb) || (data == NULL)", __FUNCTION__);
     return (rc = NCSCC_RC_FAILURE);
   }
 
-  if (lgs_is_peer_v5()) {
+  if (lgs_is_peer_v7()) {
+    data_v7 = static_cast<lgsv_ckpt_msg_v7_t *>(data);
+    lgsv_ckpt_msg_type = data_v7->header.ckpt_rec_type;
+  } else if (lgs_is_peer_v5()) {
     data_v5 = static_cast<lgsv_ckpt_msg_v5_t *>(data);
     lgsv_ckpt_msg_type = data_v5->header.ckpt_rec_type;
   } else if (lgs_is_peer_v4()) {
@@ -1834,6 +1873,7 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t 
   lgs_ckpt_stream_open_t *param;
   log_stream_t *stream;
   int pos = 0, err = 0;
+  SaNameT objectName;
 
   TRACE_ENTER();
 
@@ -1902,7 +1942,18 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t 
     stream->logFileCurrent = param->logFileCurrent;
     stream->stb_prev_actlogFileCurrent = param->logFileCurrent;
     stream->stb_logFileCurrent = param->logFileCurrent;
-    stream->isRtStream = SA_TRUE;
+
+    osaf_extended_name_lend(param->logStreamName, &objectName);
+    SaImmClassNameT className = immutil_get_className(&objectName);
+    if (className != nullptr && strcmp(className, "SaLogStreamConfig") == 0) {
+      stream->isRtStream = SA_FALSE;
+    } else {
+      stream->isRtStream = SA_TRUE;
+    }
+    if (className != nullptr) free(className);
+    // Not support adding destination name into saLogStreamOpen() parameters.
+    stream->dest_names.clear();
+    stream->stb_dest_names = "";
   }
 
   /* If configured for split file system files shall be opened on stand by
@@ -2061,12 +2112,28 @@ static uint32_t ckpt_proc_cfg_stream(lgs
   SaUint32T logFullHaltThreshold; /* !app log stream */
   SaUint32T maxFilesRotated;
   char *logFileFormat;
+  char *dest_names = nullptr;
   SaUint32T severityFilter;
   char *logFileCurrent;
 
   TRACE_ENTER();
 
-  if (lgs_is_peer_v2()) {
+  if (lgs_is_peer_v7()) {
+    lgsv_ckpt_msg_v7_t *data_v7 = static_cast<lgsv_ckpt_msg_v7_t *>(data);
+    name = data_v7->ckpt_rec.stream_cfg.name;
+    fileName = data_v7->ckpt_rec.stream_cfg.fileName;
+    pathName = data_v7->ckpt_rec.stream_cfg.pathName;
+    maxLogFileSize = data_v7->ckpt_rec.stream_cfg.maxLogFileSize;
+    fixedLogRecordSize = data_v7->ckpt_rec.stream_cfg.fixedLogRecordSize;
+    logFullAction = data_v7->ckpt_rec.stream_cfg.logFullAction;
+    logFullHaltThreshold = data_v7->ckpt_rec.stream_cfg.logFullHaltThreshold;
+    maxFilesRotated = data_v7->ckpt_rec.stream_cfg.maxFilesRotated;
+    logFileFormat = data_v7->ckpt_rec.stream_cfg.logFileFormat;
+    severityFilter = data_v7->ckpt_rec.stream_cfg.severityFilter;
+    logFileCurrent = data_v7->ckpt_rec.stream_cfg.logFileCurrent;
+    dest_names = data_v7->ckpt_rec.stream_cfg.dest_names;
+    closetime = data_v7->ckpt_rec.stream_cfg.c_file_close_time_stamp;
+  } else if (lgs_is_peer_v2()) {
     lgsv_ckpt_msg_v2_t *data_v2 = static_cast<lgsv_ckpt_msg_v2_t *>(data);
     name = data_v2->ckpt_rec.stream_cfg.name;
     fileName = data_v2->ckpt_rec.stream_cfg.fileName;
@@ -2119,6 +2186,9 @@ static uint32_t ckpt_proc_cfg_stream(lgs
   strcpy(stream->logFileFormat, logFileFormat);
   stream->severityFilter = severityFilter;
   stream->logFileCurrent = logFileCurrent;
+  stream->stb_dest_names = (dest_names == nullptr) ? "" : 
std::string{dest_names};
+  stream->dest_names = logutil::Parser(stream->stb_dest_names, ";");
+  LOG_NO("stb_dest_names = %s", stream->stb_dest_names.c_str());
 
   /* If split file mode, update standby files */
   if (lgs_is_split_file_system()) {
@@ -2178,7 +2248,10 @@ uint32_t lgs_ckpt_send_async(lgs_cb_t *c
 
   TRACE_ENTER();
 
-  if (lgs_is_peer_v5()) {
+  if (lgs_is_peer_v7()) {
+    lgsv_ckpt_msg_v7_t *ckpt_rec_v7 = static_cast<lgsv_ckpt_msg_v7_t 
*>(ckpt_rec);
+    ckpt_rec_type = ckpt_rec_v7->header.ckpt_rec_type;
+  } else if (lgs_is_peer_v5()) {
     lgsv_ckpt_msg_v5_t *ckpt_rec_v5 = static_cast<lgsv_ckpt_msg_v5_t 
*>(ckpt_rec);
     ckpt_rec_type = ckpt_rec_v5->header.ckpt_rec_type;
   } else if (lgs_is_peer_v4()) {
diff --git a/src/log/logd/lgs_mbcsv.h b/src/log/logd/lgs_mbcsv.h
--- a/src/log/logd/lgs_mbcsv.h
+++ b/src/log/logd/lgs_mbcsv.h
@@ -2,6 +2,7 @@
  * File:   lgs_mbcsv.h
  *
  * (C) Copyright 2008 The OpenSAF Foundation
+ * Copyright Ericsson AB 2014, 2017 - All Rights Reserved.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
@@ -38,9 +39,11 @@
 #define LGS_MBCSV_VERSION_2 2
 #define LGS_MBCSV_VERSION_4 4
 #define LGS_MBCSV_VERSION_5 5
+#define LGS_MBCSV_VERSION_7 7
+
 
 /* Current version */
-#define LGS_MBCSV_VERSION 5
+#define LGS_MBCSV_VERSION 7
 #define LGS_MBCSV_VERSION_MIN 1
 
 /* Checkpoint message types(Used as 'reotype' w.r.t mbcsv)  */
@@ -106,6 +109,7 @@ bool lgs_is_peer_v2();
 bool lgs_is_peer_v3();
 bool lgs_is_peer_v4();
 bool lgs_is_peer_v5();
+bool lgs_is_peer_v7();
 bool lgs_is_split_file_system();
 uint32_t lgs_mbcsv_dispatch(NCS_MBCSV_HDL mbcsv_hdl);
 void lgs_free_edu_mem(char *ptr);
diff --git a/src/log/logd/lgs_mbcsv_v7.cc b/src/log/logd/lgs_mbcsv_v7.cc
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_mbcsv_v7.cc
@@ -0,0 +1,177 @@
+/*      -*- OpenSAF  -*-
+ *
+ * (C) Copyright 2017 The OpenSAF Foundation
+ * Copyright Ericsson AB 2017 - All Rights Reserved.
+ *
+ * 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. This file and program are licensed
+ * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Author(s): Ericsson AB
+ *
+ */
+
+#include "log/logd/lgs_mbcsv_v7.h"
+
+/****************************************************************************
+ * Name          : edp_ed_cfg_stream_rec
+ *
+ * Description   : This function is an EDU program for encoding/decoding
+ *                 lgsv checkpoint cfg_update_stream log rec.
+ *
+ * Arguments     : EDU_HDL - pointer to edu handle,
+ *                 EDU_TKN - internal edu token to help encode/decode,
+ *                 POINTER to the structure to encode/decode from/to,
+ *                 data length specifying number of structures,
+ *                 EDU_BUF_ENV - pointer to buffer for encoding/decoding.
+ *                 op - operation type being encode/decode.
+ *                 EDU_ERR - out param to indicate errors in processing.
+ *
+ * Return Values : NCSCC_RC_SUCCESS/NCSCC_RC_FAILURE
+ *
+ * Notes         : None.
+ *****************************************************************************/
+
+uint32_t edp_ed_cfg_stream_rec_v7(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
+                                  NCSCONTEXT ptr, uint32_t *ptr_data_len,
+                                  EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, 
EDU_ERR *o_err) {
+  uint32_t rc = NCSCC_RC_SUCCESS;
+  lgs_ckpt_stream_cfg_v3_t *ckpt_stream_cfg_msg_ptr = NULL, 
**ckpt_stream_cfg_msg_dec_ptr;
+
+  EDU_INST_SET ckpt_stream_cfg_rec_ed_rules[] = {
+    {EDU_START, edp_ed_cfg_stream_rec_v7, 0, 0, 0, 
sizeof(lgs_ckpt_stream_cfg_v3_t), 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->name, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->fileName, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->pathName, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->maxLogFileSize, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->fixedLogRecordSize, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->haProperty, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFullAction, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFullHaltThreshold, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->maxFilesRotated, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFileFormat, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->severityFilter, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFileCurrent, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->dest_names, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->c_file_close_time_stamp, 0, NULL},
+    {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
+  };
+
+  if (op == EDP_OP_TYPE_ENC) {
+    ckpt_stream_cfg_msg_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t *>(ptr);
+  } else if (op == EDP_OP_TYPE_DEC) {
+    ckpt_stream_cfg_msg_dec_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t 
**>(ptr);
+    if (*ckpt_stream_cfg_msg_dec_ptr == NULL) {
+      *o_err = EDU_ERR_MEM_FAIL;
+      return NCSCC_RC_FAILURE;
+    }
+    memset(*ckpt_stream_cfg_msg_dec_ptr, '\0', 
sizeof(lgs_ckpt_stream_cfg_v3_t));
+    ckpt_stream_cfg_msg_ptr = *ckpt_stream_cfg_msg_dec_ptr;
+  } else {
+    ckpt_stream_cfg_msg_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t *>(ptr);
+  }
+
+  rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn,
+                           ckpt_stream_cfg_rec_ed_rules,
+                           ckpt_stream_cfg_msg_ptr,
+                           ptr_data_len,
+                           buf_env, op, o_err);
+  return rc;
+}
+
+/****************************************************************************
+ * Name          : edp_ed_ckpt_msg_v7
+ *
+ * Description   : This function is an EDU program for encoding/decoding
+ *                 lgsv checkpoint messages. This program runs the
+ *                 edp_ed_hdr_rec program first to decide the
+ *                 checkpoint message type based on which it will call the
+ *                 appropriate EDU programs for the different checkpoint
+ *                 messages.
+ *
+ * Arguments     : EDU_HDL - pointer to edu handle,
+ *                 EDU_TKN - internal edu token to help encode/decode,
+ *                 POINTER to the structure to encode/decode from/to,
+ *                 data length specifying number of structures,
+ *                 EDU_BUF_ENV - pointer to buffer for encoding/decoding.
+ *                 op - operation type being encode/decode.
+ *                 EDU_ERR - out param to indicate errors in processing.
+ *
+ * Return Values : NCSCC_RC_SUCCESS/NCSCC_RC_FAILURE
+ *
+ * Notes         : None.
+ *****************************************************************************/
+
+uint32_t edp_ed_ckpt_msg_v7(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
+                            NCSCONTEXT ptr, uint32_t *ptr_data_len, 
EDU_BUF_ENV *buf_env,
+                            EDP_OP_TYPE op, EDU_ERR *o_err) {
+  uint32_t rc = NCSCC_RC_SUCCESS;
+  lgsv_ckpt_msg_v7_t *ckpt_msg_ptr = NULL, **ckpt_msg_dec_ptr;
+
+  EDU_INST_SET ckpt_msg_ed_rules[] = {
+    {EDU_START, edp_ed_ckpt_msg_v7, 0, 0, 0, sizeof(lgsv_ckpt_msg_v7_t), 0, 
NULL},
+    {EDU_EXEC, edp_ed_header_rec, 0, 0, 0, (long)&((lgsv_ckpt_msg_v7_t 
*)0)->header, 0, NULL},
+
+    {EDU_TEST, ncs_edp_uns32, 0, 0, 0, (long)&((lgsv_ckpt_msg_v7_t 
*)0)->header, 0,
+     (EDU_EXEC_RTINE)ckpt_msg_test_type},
+
+    /* Reg Record */
+    {EDU_EXEC, edp_ed_reg_rec, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.initialize_client, 0, NULL},
+
+    /* Finalize record */
+    {EDU_EXEC, edp_ed_finalize_rec_v2, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.finalize_client, 0, NULL},
+
+    /* write log Record */
+    {EDU_EXEC, edp_ed_write_rec_v2, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.write_log, 0, NULL},
+
+    /* Open stream */
+    {EDU_EXEC, edp_ed_open_stream_rec, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_open, 0, NULL},
+
+    /* Close stream */
+    {EDU_EXEC, edp_ed_close_stream_rec_v2, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_close, 0, NULL},
+
+    /* Agent dest */
+    {EDU_EXEC, edp_ed_agent_down_rec_v2, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_cfg, 0, NULL},
+
+    /* Cfg stream */
+    {EDU_EXEC, edp_ed_cfg_stream_rec_v7, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_cfg, 0, NULL},
+
+    /* Lgs cfg */
+    {EDU_EXEC, edp_ed_lgs_cfg_rec_v5, 0, 0, static_cast<int>(EDU_EXIT),
+     (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.lgs_cfg, 0, NULL},
+
+    {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
+  };
+
+  if (op == EDP_OP_TYPE_ENC) {
+    ckpt_msg_ptr = static_cast<lgsv_ckpt_msg_v7_t *>(ptr);
+  } else if (op == EDP_OP_TYPE_DEC) {
+    ckpt_msg_dec_ptr = static_cast<lgsv_ckpt_msg_v7_t **>(ptr);
+    if (*ckpt_msg_dec_ptr == NULL) {
+      *o_err = EDU_ERR_MEM_FAIL;
+      return NCSCC_RC_FAILURE;
+    }
+    memset(*ckpt_msg_dec_ptr, '\0', sizeof(lgsv_ckpt_msg_v7_t));
+    ckpt_msg_ptr = *ckpt_msg_dec_ptr;
+  } else {
+    ckpt_msg_ptr = static_cast<lgsv_ckpt_msg_v7_t *>(ptr);
+  }
+
+  rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn, ckpt_msg_ed_rules,
+                           ckpt_msg_ptr, ptr_data_len, buf_env, op, o_err);
+
+  return rc;
+
+}
diff --git a/src/log/logd/lgs_mbcsv_v7.h b/src/log/logd/lgs_mbcsv_v7.h
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_mbcsv_v7.h
@@ -0,0 +1,67 @@
+/*      -*- OpenSAF  -*-
+ *
+ * (C) Copyright 2017 The OpenSAF Foundation
+ * Copyright Ericsson AB 2017 - All Rights Reserved.
+ *
+ * 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. This file and program are licensed
+ * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Author(s): Ericsson AB
+ *
+ */
+
+#ifndef SRC_LOG_LOGD_LGS_MBCSV_V7_H_
+#define SRC_LOG_LOGD_LGS_MBCSV_V7_H_
+
+#include "log/logd/lgs.h"
+#include "log/logd/lgs_config.h"
+#include "log/logd/lgs_mbcsv.h"
+#include "log/logd/lgs_mbcsv_v2.h"
+#include "log/logd/lgs_mbcsv_v5.h"
+
+/* Structures for Checkpoint data ver 7 (to be replicated at the standby) */
+
+typedef struct {
+  char *name;
+  char *fileName;
+  char *pathName;
+  SaUint64T maxLogFileSize;
+  SaUint32T fixedLogRecordSize;
+  SaBoolT haProperty;     /* app log stream only */
+  SaLogFileFullActionT logFullAction;
+  SaUint32T logFullHaltThreshold; /* !app log stream */
+  SaUint32T maxFilesRotated;
+  char *logFileFormat;
+  SaUint32T severityFilter;
+  char *logFileCurrent;
+  char *dest_names;
+  uint64_t c_file_close_time_stamp; /* Time in sec for file rename on Active */
+} lgs_ckpt_stream_cfg_v3_t;
+
+typedef struct {
+  lgsv_ckpt_header_t header;
+  union {
+    lgs_ckpt_initialize_msg_t initialize_client;
+    lgs_ckpt_finalize_msg_v2_t finalize_client;
+    lgs_ckpt_write_log_v2_t write_log;
+    lgs_ckpt_agent_down_v2_t agent_down;
+    lgs_ckpt_stream_open_t stream_open;
+    lgs_ckpt_stream_close_v2_t stream_close;
+    lgs_ckpt_stream_cfg_v3_t stream_cfg;
+    lgs_ckpt_lgs_cfg_v5_t lgs_cfg;
+  } ckpt_rec;
+} lgsv_ckpt_msg_v7_t;
+
+uint32_t edp_ed_cfg_stream_rec_v7(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
+                                  NCSCONTEXT ptr, uint32_t *ptr_data_len,
+                                  EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, 
EDU_ERR *o_err);
+uint32_t edp_ed_ckpt_msg_v7(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
+                            NCSCONTEXT ptr, uint32_t *ptr_data_len, 
EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, EDU_ERR *o_err);
+
+#endif  // SRC_LOG_LOGD_LGS_MBCSV_V7_H_
diff --git a/src/log/logd/lgs_stream.cc b/src/log/logd/lgs_stream.cc
--- a/src/log/logd/lgs_stream.cc
+++ b/src/log/logd/lgs_stream.cc
@@ -27,6 +27,9 @@
  * Examples can be found in this file, e.g. function fileopen(...) below
  */
 
+#include "log/logd/lgs_stream.h"
+#include <algorithm>
+
 #include "log/logd/lgs.h"
 #include "lgs_config.h"
 #include "log/logd/lgs_file.h"
@@ -480,7 +483,6 @@ int lgs_populate_log_stream(
   o_stream->logRecordId = logRecordId;
   o_stream->stb_logRecordId = 0;
   o_stream->isRtStream = SA_TRUE;
-
   o_stream->logFileFormat = strdup(logFileFormat);
   if (o_stream->logFileFormat == NULL) {
     LOG_WA("Failed to allocate memory for logFileFormat");
@@ -1492,3 +1494,53 @@ bool check_max_stream() {
   return (numb_of_streams < stream_array_size ? false:true);
 }
 
+void log_stream_add_dest_name(log_stream_t *stream,
+                              const std::vector<std::string>& names) {
+  osafassert(stream != nullptr);
+  for (const auto& it : names) {
+    stream->dest_names.push_back(it);
+  }
+
+  // Prepare stb_dest_names for checkingpoint to standby
+  log_stream_form_dest_names(stream);
+}
+
+void log_stream_replace_dest_name(log_stream_t *stream,
+                                  const std::vector<std::string>& names) {
+  osafassert(stream != nullptr);
+  stream->dest_names = names;
+  // Prepare stb_dest_names for checkingpoint to standby
+  log_stream_form_dest_names(stream);
+}
+
+void log_stream_delete_dest_name(log_stream_t *stream,
+                                 const std::vector<std::string>& names) {
+  osafassert(stream != nullptr);
+  if (names.size() != 0) {
+    for (const auto& it : names) {
+      // Remove deleted destination from internal database
+      stream->dest_names.erase(std::remove(stream->dest_names.begin(),
+                                           stream->dest_names.end(), it),
+                               stream->dest_names.end());
+    }
+  } else {
+    // Delete all destination names
+    stream->dest_names.clear();
+  }
+
+  // Prepare stb_dest_names for checkingpoint to standby
+  log_stream_form_dest_names(stream);
+}
+
+void log_stream_form_dest_names(log_stream_t* stream) {
+  osafassert(stream != nullptr);
+  std::string output{""};
+  // Form stb_dest_names before checking point to standby
+  // under format "name1;name2;etc".
+  for (const auto& it : stream->dest_names) {
+    output += it + ";";
+  }
+  // Remove last semicolon(;)
+  output[output.length() - 1] = '\0';
+  stream->stb_dest_names = output;
+}
diff --git a/src/log/logd/lgs_stream.h b/src/log/logd/lgs_stream.h
--- a/src/log/logd/lgs_stream.h
+++ b/src/log/logd/lgs_stream.h
@@ -1,6 +1,7 @@
 /*      -*- OpenSAF  -*-
  *
  * (C) Copyright 2008 The OpenSAF Foundation
+ * Copyright Ericsson AB 2014, 2017 - All Rights Reserved.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
@@ -22,6 +23,7 @@
 #include "base/ncspatricia.h"
 #include <time.h>
 #include <limits.h>
+#include <vector>
 
 #include "lgs_fmt.h"
 #include "base/osaf_extended_name.h"
@@ -83,6 +85,12 @@ typedef struct log_stream {
   std::string stb_logFileCurrent; /* Current file name used on standby */
   std::string stb_prev_actlogFileCurrent; /* current file name on active when 
previous record was written */
   uint32_t stb_curFileSize;       /* Bytes written to current log file */
+
+  // Hold vector of destname string {"name1", "name2", etc.}
+  std::vector<std::string> dest_names;
+  // Hold a list of strings separated by semicolon "name1;name2;etc"
+  // This data is used to checkpoint to standby
+  std::string stb_dest_names;
 } log_stream_t;
 
 extern uint32_t log_stream_init();
@@ -133,4 +141,12 @@ void log_free_stream_resources(log_strea
 log_stream_t *iterate_all_streams(SaBoolT &end, SaBoolT jstart);
 extern log_stream_t *log_stream_get_by_name(const std::string &name);
 
+void log_stream_add_dest_name(log_stream_t *stream,
+                              const std::vector<std::string>& names);
+void log_stream_replace_dest_name(log_stream_t *stream,
+                                  const std::vector<std::string>& names);
+void log_stream_delete_dest_name(log_stream_t *stream,
+                                 const std::vector<std::string>& names);
+void log_stream_form_dest_names(log_stream_t* stream);
+
 #endif  // LOG_LOGD_LGS_STREAM_H_

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
Opensaf-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to