src/log/Makefile.am              |    8 +-
 src/log/config/logsv_classes.xml |    7 +-
 src/log/logd/lgs_config.cc       |  169 ++++++++-
 src/log/logd/lgs_config.h        |    3 +-
 src/log/logd/lgs_dest.cc         |  707 +++++++++++++++++++++++++++++++++++++++
 src/log/logd/lgs_dest.h          |  576 +++++++++++++++++++++++++++++++
 src/log/logd/lgs_evt.cc          |   33 +
 src/log/logd/lgs_imm.cc          |  202 +++++++++-
 src/log/logd/lgs_main.cc         |    8 +
 src/log/logd/lgs_mbcsv.cc        |  103 +++++-
 src/log/logd/lgs_mbcsv.h         |    6 +-
 src/log/logd/lgs_mbcsv_v5.cc     |   10 +
 src/log/logd/lgs_mbcsv_v7.cc     |  177 +++++++++
 src/log/logd/lgs_mbcsv_v7.h      |   67 +++
 src/log/logd/lgs_stream.cc       |   60 +++-
 src/log/logd/lgs_stream.h        |   16 +
 src/log/logd/lgs_util.cc         |   63 +++
 src/log/logd/lgs_util.h          |   11 +-
 18 files changed, 2154 insertions(+), 72 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
@@ -72,9 +72,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
@@ -118,10 +120,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_util.h"
 
 static SaVersionT immVersion = { 'A', 2, 11 };
 
@@ -709,33 +710,162 @@ static int lgs_cfg_verify_log_filesys_co
   return rc;
 }
 
+//>
+// Utility functions to validate destination configuration format
+//<
+// Typedef for shorten declaration
+// This type could be used to hold set of strings with formats
+// 1) {"name;type;value", etc.}
+// 2) {"name", "type", "value"}
+// 3) etc.
+using VectorString = std::vector<std::string>;
+//>
+// Define the "token" possition in destination configuration.
+// kName : use this index to get "name" token
+// kType : use this index to get "type" token
+// kValue: use this index to get "value" token
+// kSize : this is the maximum size of the dest configuration vector.
+//<
+enum { kName = 0, kType, kValue, kSize };
+// Tokens seperator
+const char kSemicolon[] = ";";
+
+
+// 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) {
+      TRACE("%s wrong destination format", __func__);
+      return false;
+    }
+  }
+  return true;
+}
+
+// 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) {
+      TRACE("%s wrong name format (name is empty value)", __func__);
+      return false;
+    }
+    // invalid name if having special characters in
+    if (logutil::isUnixName(sdes[kName]) == false) {
+      TRACE("%s wrong name format (has special chars in)", __func__);
+      return false;
+    }
+  }
+  return true;
+}
+
+class LocalSocketType;
+
+// 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) {
+      TRACE("%s wrong type format (type is empty value)", __func__);
+      return false;
+    }
+    // Only support type = "localsocket"
+    if (sdes[kType] != "localsocket") {
+      TRACE("%s wrong type format (only support type = localsocket)", 
__func__);
+      return false;
+    }
+  }
+  return true;
+}
+
+// Return true if no dulicated info, otherwise false.
+bool is_no_config_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]) {
+        TRACE("%s name is duplicated", __func__);
+        return false;
+      }
+      // Ignore nildest
+      if ((sdes[kValue].length() == 0) || (sdes2[kValue].length() == 0))
+        continue;
+      // Duplicate "value"
+      if (sdes2[kValue] == sdes[kValue]) {
+        TRACE("%s value is duplicated", __func__);
+        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,
+                                    SaImmAttrModificationTypeT type) {
+  if (type == SA_IMM_ATTR_VALUES_DELETE) return true;
+  // No checking duplicated if replacing to one value
+  if (type == SA_IMM_ATTR_VALUES_REPLACE && vdest.size() < 2) return true;
+  // Check in all replace values if any duplicated name/value
+  if (type == SA_IMM_ATTR_VALUES_REPLACE)  {
+    // Check whethere there is duplicated "name" or "value"
+    // in list of provided destination configurations.
+    return is_no_config_duplicated(vdest, vdest);
+  } else {
+    bool isNoDuplicated = true;
+    // Check whethere there is duplicated "name" or "value"
+    // in adding destination configurations and existing ones.
+    // Firstly, check if any duplicate in added items.
+    if (vdest.size() > 1) {
+      isNoDuplicated = is_no_config_duplicated(vdest, vdest);
+      if (isNoDuplicated == false) return false;
+    }
+    // Secondly, check if any duplicate in added items with existing list.
+    return is_no_config_duplicated(
+        vdest, lgs_conf.logRecordDestinationConfiguration);
+  }
+}
+
 /**
  * Verify all values of log_record_destination_configuration
  * Rules:
  * - Empty string is Ok else
- * - String shall have at least three fields separated by '\n'
- * - First and second field cannot be empty
+ * - The destination configiration must be in right format
+ * - "name" token must be valid
+ * - "type" token must be valid
+ * - Must no duplicated info
  *
  * @param log_record_destination_configuration[in]
  * @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;
-    }
-  }
-
-  TRACE_LEAVE2("rc = %s", rc == -1? "Fail": "Pass");
-  return rc;
+    std::vector<std::string>& vdest,
+    SaImmAttrModificationTypeT 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, type)
+                 );
+  return (result == true) ? (0) : (-1);
 }
 
 
@@ -846,7 +976,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;
     rc = -1;
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
@@ -304,7 +304,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,707 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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/logd/lgs_util.h"
+#include "log/logd/lgs_config.h"
+#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)
+    : sock_path_{socket_name}
+    , sock_{socket_name}
+    , status_{DestinationStatus::kInactive} {
+      // Open the unix socket & and flush the destination status to @status_
+      Open();
+};
+
+void LocalSocketHandler::FlushStatus() {
+  if (sock_.fd() >= 0) {
+    status_ = DestinationStatus::kActive;
+  } else {
+    status_ = DestinationStatus::kFailed;
+  }
+}
+
+DestinationStatus LocalSocketHandler::GetSockStatus() {
+  Open();
+  return status_;
+}
+
+void LocalSocketHandler::Open() {
+  FlushStatus();
+}
+
+void LocalSocketHandler::FormRfc5424(
+    const DestinationHandler::RecordInfo& msg,
+    RfcBuffer* buf) {
+  base::LogMessage::Severity sev{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 = sock_.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 = sock_.Send(buffer.data(), length);
+  }
+
+  FlushStatus();
+
+  TRACE_LEAVE();
+  return ((len != length) ? (ErrCode::kErr) : (ErrCode::kOk));
+}
+
+void LocalSocketHandler::Close() {
+  // Do nothing
+}
+
+// Convert AIS sev @a sev to Severity class
+base::LogMessage::Severity LocalSocketHandler::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;
+  }
+}
+
+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::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";
+  }
+}
+
+const std::string LocalSocketType::GetDestinationStatus(
+    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 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_.GetDestinationStatus(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;
+  }
+}
+
+std::string DestinationHandler::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.
+  //           Why 31? It is due to MSGID field length limitation
+  //           (max is 32 chars length).
+  // 2) Otherwise, generate a hash number from stream DN
+  //    if stream name is 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;
+}
+
+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: {
+      const CfgDestMsg& cdest = msg.info.cfg;
+      TRACE("%s kCfgDest (%s)", __func__, cdest.name);
+      nametype_map_[std::string{cdest.name}] =
+          GetDestType(std::string{cdest.type});
+      break;
+    }
+
+    case MsgType::kDelDest: {
+      const DelDestMsg& deldest = msg.info.del;
+      TRACE("%s kDelDest (%s)", __func__, deldest.name);
+      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);
+    UpdateRtDestStatus();
+    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);
+  }
+
+  UpdateRtDestStatus();
+  return rc;
+}
+
+// Update the `logRecordDestinationStatus` here whenever
+// getting CfgDestination or WriteToDestination (?)
+void DestinationHandler::UpdateRtDestStatus() {
+  VectorString vstatus = GetDestinationStatus();
+  lgs_config_chg_t config_data = {NULL, 0};
+  lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_STATUS, vstatus, 
&config_data);
+  // Update configuration data store, no need to checkpoint the status to stb.
+  int ret = lgs_cfg_update(&config_data);
+  if (ret == -1) {
+    LOG_WA("%s lgs_cfg_update Fail", __func__);
+  }
+}
+
+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();
+}
+
+//==============================================================================
+// 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) {
+    TRACE("%s No destination goes with this stream (%s)", __func__, data.name);
+    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>]
+  // hostname = where the log record comes from (not active node)
+  const std::string origin = std::string {data.hostname} + networkname;
+  const std::string msgid = DestinationHandler::Instance().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,576 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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;type;value".
+// 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;type;value", 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;b;c", "d;e;f"}
+// 2) Case #2: modify one destinations. E.g: dest name @d -> @d1
+//    Then, @type = ModifyType::kReplace and @vdest contains data {"d1;e;f"}
+// 3) Case #3: delete one destination. Eg: dest name @a
+//    Then, @type = ModifyType::kDelete and @vdest should contains {"a;b;c"}
+// 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();
+
+//>
+// @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_; }
+  // Extract the stream name from stream DN
+  static std::string GenerateMsgId(const std::string&, bool);
+
+  // 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;type;value".
+  void FormCfgDestMsg(const std::string& dest, CfgDestMsg* msg);
+  // Pack @DelDestMsg basing on pure destination format
+  // "name;type;value".
+  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&);
+  // Update the `logRecordDestinationStatus`
+  void UpdateRtDestStatus();
+
+  // The map between destination @name and destination @type
+  NameTypeMap nametype_map_;
+  // Hold all existing destinations with pure format {"a;b;c", 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:
+  // 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();
+  // Convert AIS log stream severity to syslog severity
+  static base::LogMessage::Severity Sev(uint16_t);
+
+  // Hold destination info (@value)
+  std::string sock_path_;
+  base::UnixClientSocket sock_;
+  // 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 GetDestinationStatus(const std::string& name);
+  // Convert enum status to string
+  const std::string DestStatusToStr(DestinationStatus status);
+  // 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;
@@ -854,13 +878,14 @@ static SaAisErrorT config_ccb_completed_
       // Note: Multi value attribute
       TRACE("logRecordDestinationConfiguration. Values number = %d",
             attribute->attrValuesNumber);
-      std::vector<std::string> values_vector;
+      std::vector<std::string> values_vector{};
       for (uint32_t i=0; i < attribute->attrValuesNumber; i++) {
         value = attribute->attrValues[i];
         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) {
@@ -1974,6 +2033,41 @@ static void apply_conf_logDataGroupname(
   logDataGroupname_fileown(value_ptr);
 }
 
+static void apply_config_destinations_change(
+    const std::vector<std::string>& vdestcfg,
+    SaImmAttrModificationTypeT type,
+    lgs_config_chg_t* config_data) {
+  switch (type) {
+    case SA_IMM_ATTR_VALUES_ADD:
+      // Configure destinations
+      CfgDestination(vdestcfg, ModifyType::kAdd);
+      lgs_cfgupd_multival_add(LOG_RECORD_DESTINATION_CONFIGURATION,
+                              vdestcfg,
+                              config_data);
+      break;
+
+    case SA_IMM_ATTR_VALUES_DELETE:
+      // Configure destinations
+      CfgDestination(vdestcfg, ModifyType::kDelete);
+      lgs_cfgupd_multival_delete(LOG_RECORD_DESTINATION_CONFIGURATION,
+                                 vdestcfg,
+                                 config_data);
+      break;
+
+    case SA_IMM_ATTR_VALUES_REPLACE:
+      CfgDestination(vdestcfg, ModifyType::kReplace);
+      lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_CONFIGURATION,
+                                 vdestcfg,
+                                 config_data);
+      break;
+
+    default:
+      // Shall never happen
+      LOG_ER("%s: Unknown modType %d", __FUNCTION__, type);
+      osafassert(0);
+  };
+}
+
 /**
  * Apply changes. Validation is not needed here since all validation is done in
  * the complete callback
@@ -2088,28 +2182,9 @@ static void config_ccb_apply_modify(cons
         char *value_str = *(reinterpret_cast<char **>(value));
         values_vector.push_back(value_str);
       }
-
-      switch (attrMod->modType) {
-        case SA_IMM_ATTR_VALUES_ADD:
-          lgs_cfgupd_multival_add(LOG_RECORD_DESTINATION_CONFIGURATION,
-                                  values_vector,
-                                  &config_data);
-          break;
-        case SA_IMM_ATTR_VALUES_DELETE:
-          lgs_cfgupd_multival_delete(LOG_RECORD_DESTINATION_CONFIGURATION,
-                                  values_vector,
-                                  &config_data);
-          break;
-        case SA_IMM_ATTR_VALUES_REPLACE:
-          lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_CONFIGURATION,
-                                  values_vector,
-                                  &config_data);
-          break;
-        default:
-          // Shall never happen
-          LOG_ER("%s: Unknown modType %d", __FUNCTION__, attrMod->modType);
-          osafassert(0);
-      };
+      apply_config_destinations_change(values_vector,
+                                       attrMod->modType,
+                                       &config_data);
     }
 
     attrMod = opdata->param.modify.attrMods[i++];
@@ -2250,6 +2325,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 +2388,30 @@ static void stream_ccb_apply_create(cons
   TRACE_LEAVE();
 }
 
+static void apply_destination_names_change(
+    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 +2431,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 +2479,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_names_change(stream, vstring, attrMod->modType);
     } else {
       LOG_ER("Error: Unknown attribute name");
       osafassert(0);
@@ -2613,6 +2738,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 +2822,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_main.cc b/src/log/logd/lgs_main.cc
--- a/src/log/logd/lgs_main.cc
+++ b/src/log/logd/lgs_main.cc
@@ -39,6 +39,7 @@
 #include "lgs_recov.h"
 #include "osaf/immutil/immutil.h"
 #include "lgs_clm.h"
+#include "log/logd/lgs_dest.h"
 
 /* ========================================================================
  *   DEFINITIONS
@@ -371,6 +372,7 @@ done:
 uint32_t initialize_for_assignment(lgs_cb_t *cb, SaAmfHAStateT ha_state) {
   const char *logsv_root_dir = NULL;
   const char *logsv_data_groupname = NULL;
+  const VectorString *vdest = nullptr;
   TRACE_ENTER2("ha_state = %d", (int) ha_state);
   uint32_t rc = NCSCC_RC_SUCCESS;
 
@@ -393,6 +395,12 @@ uint32_t initialize_for_assignment(lgs_c
   logsv_data_groupname = static_cast<const char 
*>(lgs_cfg_get(LGS_IMM_DATA_GROUPNAME));
   LOG_NO("LOG root directory is: \"%s\"", logsv_root_dir);
   LOG_NO("LOG data group is: \"%s\"", logsv_data_groupname);
+  vdest = reinterpret_cast<const VectorString*>(
+      lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION));
+  osafassert(vdest != nullptr);
+  if (vdest->size() > 1) {
+    CfgDestination(*vdest, ModifyType::kAdd);
+  }
 
   /* Initialize file handling thread
    * Configuration must have been initialized
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;
@@ -1069,11 +1095,16 @@ static uint32_t ckpt_decode_log_cfg(lgs_
   uint32_t rc = NCSCC_RC_SUCCESS;
   void *lgs_cfg = NULL;
   EDU_PROG_HANDLER edp_function = NULL;
+  lgsv_ckpt_msg_v7_t *ckpt_msg_v7;
   lgsv_ckpt_msg_v5_t *ckpt_msg_v5;
   lgsv_ckpt_msg_v3_t *ckpt_msg_v3;
   lgsv_ckpt_msg_v2_t *ckpt_msg_v2;
 
-  if (lgs_is_peer_v5()) {
+  if (lgs_is_peer_v7()) {
+    ckpt_msg_v7 = static_cast<lgsv_ckpt_msg_v7_t *>(ckpt_msg);
+    lgs_cfg = &ckpt_msg_v7->ckpt_rec.lgs_cfg;
+    edp_function = edp_ed_lgs_cfg_rec_v5;
+  } else if (lgs_is_peer_v5()) {
     ckpt_msg_v5 = static_cast<lgsv_ckpt_msg_v5_t *>(ckpt_msg);
     lgs_cfg = &ckpt_msg_v5->ckpt_rec.lgs_cfg;
     edp_function = edp_ed_lgs_cfg_rec_v5;
@@ -1111,6 +1142,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 +1163,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 +1184,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 +1211,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 +1457,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 +1878,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 +1947,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 +2117,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 +2191,14 @@ static uint32_t ckpt_proc_cfg_stream(lgs
   strcpy(stream->logFileFormat, logFileFormat);
   stream->severityFilter = severityFilter;
   stream->logFileCurrent = logFileCurrent;
+  TRACE("dest_names: %s", dest_names);
+  if (strlen(dest_names) == 0) {
+    stream->stb_dest_names = "";
+    stream->dest_names.clear();
+  } else {
+    stream->stb_dest_names = std::string{dest_names};
+    stream->dest_names = logutil::Parser(stream->stb_dest_names, ";");
+  }
 
   /* If split file mode, update standby files */
   if (lgs_is_split_file_system()) {
@@ -2178,7 +2258,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_v5.cc b/src/log/logd/lgs_mbcsv_v5.cc
--- a/src/log/logd/lgs_mbcsv_v5.cc
+++ b/src/log/logd/lgs_mbcsv_v5.cc
@@ -1,6 +1,7 @@
 /*      -*- OpenSAF  -*-
  *
  * (C) Copyright 2015 The OpenSAF Foundation
+ * Copyright Ericsson AB 2015, 2017 - All Rights Reserved.
  * File:   lgs_mbcsv_v5.c
  *
  * This program is distributed in the hope that it will be useful, but
@@ -22,6 +23,7 @@
  */
 
 #include "lgs_mbcsv_v5.h"
+#include "log/logd/lgs_dest.h"
 
 /****************************************************************************
  * Name          : ckpt_proc_lgs_cfg
@@ -132,6 +134,14 @@ uint32_t ckpt_proc_lgs_cfg_v5(lgs_cb_t *
     TRACE("\tUpdating mailbox limits");
     (void) lgs_configure_mailbox();
   }
+  // Only support destination configuration since V7
+  if (lgs_is_peer_v7()) {
+    const VectorString *vdest = nullptr;
+    vdest = reinterpret_cast<const VectorString*>(
+        lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION));
+    osafassert(vdest != nullptr);
+    CfgDestination(*vdest, ModifyType::kReplace);
+  }
 
   /* Free buffer allocated by the EDU encoder */
   if (param->buffer == NULL) {
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");
@@ -654,6 +656,7 @@ log_stream_t *log_stream_new(const std::
   stream->creationTimeStamp = lgs_get_SaTime();
   stream->severityFilter = 0x7f;  /* by default all levels are allowed */
   stream->isRtStream = SA_FALSE;
+  stream->dest_names.clear();
 
   /* Initiate local or shared stream file descriptor dependant on shared or
    * split file system
@@ -1492,3 +1495,58 @@ 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{""};
+  bool addSemicolon = false;
+  // Form stb_dest_names before checking point to standby
+  // under format "name1;name2;etc".
+  for (const auto& it : stream->dest_names) {
+    output += it + ";";
+    addSemicolon = true;
+  }
+  if (addSemicolon == true) {
+    osafassert(output.length() > 0);
+    // 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_
diff --git a/src/log/logd/lgs_util.cc b/src/log/logd/lgs_util.cc
--- a/src/log/logd/lgs_util.cc
+++ b/src/log/logd/lgs_util.cc
@@ -835,3 +835,66 @@ bool lgs_is_extended_name_valid(const Sa
 
   return true;
 }
+
+//==============================================================================
+// logutil namespace
+//==============================================================================
+namespace logutil {
+
+// Split a string @str with delimiter @delimiter
+// and return an vector of strings.
+std::vector<std::string> Parser(const std::string& str,
+                                const std::string& delimiter) {
+  std::vector<std::string> 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;
+}
+
+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;
+}
+
+};  // namespace logutil
diff --git a/src/log/logd/lgs_util.h b/src/log/logd/lgs_util.h
--- a/src/log/logd/lgs_util.h
+++ b/src/log/logd/lgs_util.h
@@ -26,7 +26,7 @@
 
 #include "osaf/saf/saAis.h"
 #include "amf/saf/saAmf.h"
-
+#include <vector>
 #include "lgs_stream.h"
 #include "lgs_evt.h"
 
@@ -83,4 +83,13 @@ void lgs_close_timer(int ufd);
 
 bool lgs_is_extended_name_valid(const SaNameT* name);
 
+namespace logutil {
+// Parse string format "a;b;c" to vector of string {a, b, c}
+// In generic, this function could be used to split a string
+// separated by delimiter to a vector of strings.
+std::vector<std::string> Parser(const std::string&, const std::string&);
+// Check if @name contain special characters in.
+bool isUnixName(const std::string& name);
+};  // namespace logutil
+
 #endif  // LOG_LOGD_LGS_UTIL_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