Hi Vu,

5.1 & 5.2 Release not related  our parallel implementations 5.2 details ,
one release one version , so #2146 & #2258 both shoud use 
LGS_MBCSV_VERSION_6 6 ,
please do change .

-AVM

On 2/24/2017 12:17 PM, Vu Minh Nguyen wrote:
> Hi Mahesh,
>
> As we did parallel implementations for two separated ticket #2146 & #2258,
> and pushing the codes independently,
> it is inconvenience or cannot use same MBCSV version for both.
>
> Regards, Vu
>
>> -----Original Message-----
>> From: A V Mahesh [mailto:[email protected]]
>> Sent: Friday, February 24, 2017 1:41 PM
>> To: Vu Minh Nguyen <[email protected]>;
>> [email protected]; [email protected]
>> Cc: [email protected]
>> Subject: Re: [PATCH 1 of 2] log: add alternative destinations of log
> records
>> [#2258]
>>
>> Hi Vu/Lennart,
>>
>> On 2/24/2017 7:38 AM, Vu Minh Nguyen wrote:
>>>    #define LGS_MBCSV_VERSION_6 6
>>> +#define LGS_MBCSV_VERSION_7 7
>> In one release 5.2  and   2 enhancement  #2146 & #2258,  why they are
>> using two different
>> LGS_MBCSV_VERSION = 6  &  LGS_MBCSV_VERSION = 7  ,  #2146 & #2258
>> both
>> can use sing  LGS_MBCSV_VERSION = 6
>>
>> -AVM
>>
>>
>> On 2/24/2017 7:38 AM, Vu Minh Nguyen wrote:
>>>    src/log/Makefile.am               |   14 +-
>>>    src/log/config/logsv_classes.xml  |    7 +-
>>>    src/log/logd/lgs_common.h         |   81 +++++++
>>>    src/log/logd/lgs_config.cc        |  173 ++++++++++++++-
>>>    src/log/logd/lgs_config.h         |    3 +-
>>>    src/log/logd/lgs_dest.cc          |  409
>> ++++++++++++++++++++++++++++++++++++++
>>>    src/log/logd/lgs_dest.h           |  294 +++++++++++++++++++++++++++
>>>    src/log/logd/lgs_evt.cc           |   32 ++
>>>    src/log/logd/lgs_imm.cc           |  211 ++++++++++++++++---
>>>    src/log/logd/lgs_main.cc          |   11 +-
>>>    src/log/logd/lgs_mbcsv.cc         |  230 +++++++++++++++++----
>>>    src/log/logd/lgs_mbcsv.h          |    5 +-
>>>    src/log/logd/lgs_mbcsv_v5.cc      |   10 +
>>>    src/log/logd/lgs_mbcsv_v7.cc      |  177 ++++++++++++++++
>>>    src/log/logd/lgs_mbcsv_v7.h       |   66 ++++++
>>>    src/log/logd/lgs_nildest.cc       |  153 ++++++++++++++
>>>    src/log/logd/lgs_nildest.h        |   72 ++++++
>>>    src/log/logd/lgs_stream.cc        |   62 +++++-
>>>    src/log/logd/lgs_stream.h         |   16 +
>>>    src/log/logd/lgs_unixsock_dest.cc |  343
>> +++++++++++++++++++++++++++++++
>>>    src/log/logd/lgs_unixsock_dest.h  |  245 ++++++++++++++++++++++
>>>    src/log/logd/lgs_util.cc          |   34 +++
>>>    src/log/logd/lgs_util.h           |   11 +-
>>>    23 files changed, 2550 insertions(+), 109 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) Add NILDEST destination type
>>> 4) 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
>>> @@ -73,9 +73,15 @@ noinst_HEADERS += \
>>>     src/log/logd/lgs_mbcsv_v3.h \
>>>     src/log/logd/lgs_mbcsv_v5.h \
>>>     src/log/logd/lgs_mbcsv_v6.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 \
>>> +   src/log/logd/lgs_nildest.h \
>>> +   src/log/logd/lgs_unixsock_dest.h \
>>> +   src/log/logd/lgs_common.h
>>> +
>>>
>>>    bin_PROGRAMS += bin/saflogger
>>>    osaf_execbin_PROGRAMS += bin/osaflogd
>>> @@ -120,10 +126,14 @@ bin_osaflogd_SOURCES = \
>>>     src/log/logd/lgs_mbcsv_v3.cc \
>>>     src/log/logd/lgs_mbcsv_v5.cc \
>>>     src/log/logd/lgs_mbcsv_v6.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 \
>>> +   src/log/logd/lgs_nildest.cc \
>>> +   src/log/logd/lgs_unixsock_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_common.h b/src/log/logd/lgs_common.h
>>> new file mode 100644
>>> --- /dev/null
>>> +++ b/src/log/logd/lgs_common.h
>>> @@ -0,0 +1,81 @@
>>> +/*      -*- 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_COMMON_H_
>>> +#define SRC_LOG_LOGD_LGS_COMMON_H_
>>> +
>>> +#include <string>
>>> +#include <vector>
>>> +
>>> +// 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
>>> +  // or not given any configuration value.
>>> +  kFailed = 1,
>>> +};
>>> +
>>> +// 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.
>>> +// Their meanings are equivelent to `SaImmAttrModificationTypeT`
>>> +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 "type"
>>> +// kValue: use this index to get "value"
>>> +// 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>;
>>> +
>>> +#endif  // SRC_LOG_LOGD_LGS_COMMON_H_
>>> 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
>>> @@ -709,36 +709,168 @@ 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::isValidName(sdes[kName]) == false) {
>>> +      TRACE("%s wrong name format (has special chars in)", __func__);
>>> +      return false;
>>> +    }
>>> +
>>> +    if (sdes[kName].length() > 255) {
>>> +      TRACE("%s too long name", __func__);
>>> +      return false;
>>> +    }
>>> +  }
>>> +  return true;
>>> +}
>>> +
>>> +// Return false if "type" token is invalid
>>> +bool is_type_valid(const VectorString& vdest) {
>>> +  // Check each single destination
>>> +  for (const auto& it : vdest) {
>>> +    const VectorString sdes = logutil::Parser(it, kSemicolon);
>>> +    if (sdes[kType].length() == 0) {
>>> +      TRACE("%s wrong type format (type is empty value)", __func__);
>>> +      return false;
>>> +    }
>>> +    // Only support type = "UNIX_SOCKET"
>>> +    if (sdes[kType] != "UNIX_SOCKET" && sdes[kType] != "NILDEST") {
>>> +      TRACE("%s wrong type format. Only UNIX_SOCKET and NILDEST
>> supported",
>>> +            __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);
>>>    }
>>>
>>> -
>>>    /**
>>>     * Verify logRootDirectory; path to be used as log root directory
>>>     * Rules:
>>> @@ -846,7 +978,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,409 @@
>>> +/*      -*- 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/logd/lgs_nildest.h"
>>> +#include "log/logd/lgs_unixsock_dest.h"
>>> +#include "base/logtrace.h"
>>> +#include "base/hash.h"
>>> +
>>> +static const char kDelimeter[] = ";";
>>> +
>>>
>> +//============================================================
>> ==================
>>> +// DestinationHandler class
>>>
>> +//============================================================
>> ==================
>>> +DestinationHandler DestinationHandler::me_;
>>> +
>>> +// Only support "kUnix" and "kNilDest" so far
>>> +DestinationHandler::DestType DestinationHandler::GetDestType(
>>> +    const std::string& type) {
>>> +  if (type == UnixSocketType::Type()) {
>>> +    return DestType::kUnix;
>>> +  } else if (type == NilDestType::Type()) {
>>> +    return DestType::kNilDest;
>>> +  } 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 = base::Hash(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) {
>>> +    NilDestType::Instance().ProcessMsg(msg);
>>> +    rc = UnixSocketType::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 @UnixSocketType.
>>> +  // 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 = UnixSocketType::Instance().ProcessMsg(msg);
>>> +      break;
>>> +
>>> +    case DestType::kNilDest:
>>> +      rc = NilDestType::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() {
>>> +  VectorString unixsock_type{};
>>> +  VectorString nildest_type{};
>>> +  // Go thought all 02 supported destination types
>>> +  unixsock_type = UnixSocketType::Instance().GetAllDestStatus();
>>> +  nildest_type = NilDestType::Instance().GetAllDestStatus();
>>> +  unixsock_type.insert(unixsock_type.end(), nildest_type.begin(),
>>> +                       nildest_type.end());
>>> +
>>> +  return unixsock_type;
>>> +}
>>> +
>>>
>> +//============================================================
>> ==================
>>> +// 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,294 @@
>>> +/*      -*- 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 <string>
>>> +#include <map>
>>> +
>>> +#include "log/logd/lgs_common.h"
>>> +#include "base/time.h"
>>> +
>>> +//>
>>> +// @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, kNilDest, 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_;
>>> +};
>>> +
>>> +#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
>>> @@ -29,6 +29,7 @@
>>>    #include "lgs_imm_gcfg.h"
>>>    #include "base/osaf_extended_name.h"
>>>    #include "lgs_clm.h"
>>> +#include "lgs_dest.h"
>>>
>>>    static uint32_t process_api_evt(lgsv_lgs_evt_t *evt);
>>>    static uint32_t proc_lga_updn_mds_msg(lgsv_lgs_evt_t *evt);
>>> @@ -1282,6 +1283,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);
>>> @@ -1341,6 +1344,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;
>>> @@ -856,13 +880,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);
>>> @@ -1270,6 +1295,29 @@ done:
>>>      return rc;
>>>    }
>>>
>>> +static bool is_valid_dest_names(const std::vector<std::string>& names)
> {
>>> +  // Stream has no destination name
>>> +  if (names.size() == 0) return true;
>>> +  for (const auto& name : names) {
>>> +    // Empty destination name is invalid
>>> +    if (name.empty() == true || name.size() == 0) {
>>> +      TRACE("%s name is empty", __func__);
>>> +      return false;
>>> +    }
>>> +    // Contain special characters is not allowed
>>> +    if (logutil::isValidName(name) == false) {
>>> +      TRACE("%s name has special chars in", __func__);
>>> +      return false;
>>> +    }
>>> +    // Name is too long
>>> +    if (name.length() > 255) {
>>> +      TRACE("%s too long name", __func__);
>>> +      return false;
>>> +    }
>>> +  }
>>> +  return true;
>>> +}
>>> +
>>>    /**
>>>     * Validate input parameters creation and modify of a persistent
> stream
>>>     * i.e. a stream that has a configuration object.
>>> @@ -1368,12 +1416,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.
>>> @@ -1428,8 +1480,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) {
>>> @@ -1976,6 +2044,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
>>> @@ -2090,28 +2193,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++];
>>> @@ -2252,6 +2336,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++;
>>> @@ -2306,6 +2399,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;
>>> @@ -2325,12 +2442,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);
>>> @@ -2369,6 +2495,14 @@ static void stream_ccb_apply_modify(cons
>>>          if (stream->streamType != STREAM_TYPE_ALARM &&
>>>                    stream->streamType != STREAM_TYPE_NOTIFICATION)
>>>            lgs_send_severity_filter_to_clients(stream->streamId,
> 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);
>>> @@ -2620,6 +2754,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
>>> @@ -2703,6 +2838,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
>>> @@ -1,6 +1,7 @@
>>> -/*      -*- OpenSAF  -*-
>>> + /*      -*- OpenSAF  -*-
>>>     *
>>>     * (C) Copyright 2008-2010 The OpenSAF Foundation
>>> + * Copyright Ericsson AB 2018, 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
>>> @@ -39,6 +40,7 @@
>>>    #include "lgs_recov.h"
>>>    #include "osaf/immutil/immutil.h"
>>>    #include "lgs_clm.h"
>>> +#include "log/logd/lgs_dest.h"
>>>
>>>    /*
>> ==============================================================
>> ==========
>>>     *   DEFINITIONS
>>> @@ -371,6 +373,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 std::vector<std::string> *vdest = nullptr;
>>>      TRACE_ENTER2("ha_state = %d", (int) ha_state);
>>>      uint32_t rc = NCSCC_RC_SUCCESS;
>>>
>>> @@ -393,6 +396,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 std::vector<std::string>*>(
>>> +
>> lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION));
>>> +  osafassert(vdest != nullptr);
>>> +  if (vdest->size() > 0) {
>>> +    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
>>> @@ -21,6 +21,9 @@
>>>    #include "base/ncssysf_mem.h"
>>>    #include "base/osaf_time.h"
>>>
>>> +#include "osaf/immutil/immutil.h"
>>> +#include "log/logd/lgs_dest.h"
>>> +#include "log/logd/lgs_mbcsv_v7.h"
>>>    #include "lgs_mbcsv_v6.h"
>>>    #include "lgs_mbcsv_v5.h"
>>>    #include "lgs_mbcsv_v3.h"
>>> @@ -125,43 +128,84 @@ uint32_t edp_ed_open_stream_rec(EDU_HDL
>>>                                    EDU_BUF_ENV *buf_env, EDP_OP_TYPE op,
> EDU_ERR
>> *o_err) {
>>>      uint32_t rc = NCSCC_RC_SUCCESS;
>>>      lgs_ckpt_stream_open_t *ckpt_open_stream_msg_ptr = NULL,
>> **ckpt_open_stream_msg_dec_ptr;
>>> +  if (lgs_is_peer_v7()) {
>>> +    EDU_INST_SET ckpt_open_stream_rec_ed_rules[] = {
>>> +      {EDU_START, edp_ed_open_stream_rec, 0, 0, 0,
>> sizeof(lgs_ckpt_stream_open_t), 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->streamId, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->clientId, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFile, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logPath, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFileCurrent, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->dest_names, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxFileSize, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_int32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxLogRecordSize, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_int32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFileFullAction, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_int32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxFilesRotated, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->fileFmt, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logStreamName, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->creationTimeStamp, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->numOpeners, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->streamType, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logRecordId, 0, NULL},
>>> +      {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
>>> +    };
>>>
>>> -  EDU_INST_SET ckpt_open_stream_rec_ed_rules[] = {
>>> -    {EDU_START, edp_ed_open_stream_rec, 0, 0, 0,
>> sizeof(lgs_ckpt_stream_open_t), 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->streamId, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->clientId, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFile, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->logPath, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFileCurrent, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxFileSize, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxLogRecordSize, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFileFullAction, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxFilesRotated, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->fileFmt, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->logStreamName, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->creationTimeStamp, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->numOpeners, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->streamType, 0, NULL},
>>> -    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t
>> *)0)->logRecordId, 0, NULL},
>>> -    {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
>>> -  };
>>> +    if (op == EDP_OP_TYPE_ENC) {
>>> +      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t
>> *>(ptr);
>>> +    } else if (op == EDP_OP_TYPE_DEC) {
>>> +      ckpt_open_stream_msg_dec_ptr =
>> static_cast<lgs_ckpt_stream_open_t **>(ptr);
>>> +      if (*ckpt_open_stream_msg_dec_ptr == NULL) {
>>> +        *o_err = EDU_ERR_MEM_FAIL;
>>> +        return NCSCC_RC_FAILURE;
>>> +      }
>>> +      memset(*ckpt_open_stream_msg_dec_ptr, '\0',
>> sizeof(lgs_ckpt_stream_open_t));
>>> +      ckpt_open_stream_msg_ptr = *ckpt_open_stream_msg_dec_ptr;
>>> +    } else {
>>> +      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t
>> *>(ptr);
>>> +    }
>>>
>>> -  if (op == EDP_OP_TYPE_ENC) {
>>> -    ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t
>> *>(ptr);
>>> -  } else if (op == EDP_OP_TYPE_DEC) {
>>> -    ckpt_open_stream_msg_dec_ptr = static_cast<lgs_ckpt_stream_open_t
>> **>(ptr);
>>> -    if (*ckpt_open_stream_msg_dec_ptr == NULL) {
>>> -      *o_err = EDU_ERR_MEM_FAIL;
>>> -      return NCSCC_RC_FAILURE;
>>> +    rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn,
>> ckpt_open_stream_rec_ed_rules, ckpt_open_stream_msg_ptr,
>>> +                             ptr_data_len, buf_env, op, o_err);
>>> +
>>> +  } else {
>>> +    EDU_INST_SET ckpt_open_stream_rec_ed_rules[] = {
>>> +      {EDU_START, edp_ed_open_stream_rec, 0, 0, 0,
>> sizeof(lgs_ckpt_stream_open_t), 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->streamId, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->clientId, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFile, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logPath, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFileCurrent, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxFileSize, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_int32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxLogRecordSize, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_int32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logFileFullAction, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_int32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->maxFilesRotated, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->fileFmt, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_string, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logStreamName, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->creationTimeStamp, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->numOpeners, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->streamType, 0, NULL},
>>> +      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0,
> (long)&((lgs_ckpt_stream_open_t
>> *)0)->logRecordId, 0, NULL},
>>> +      {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
>>> +    };
>>> +
>>> +    if (op == EDP_OP_TYPE_ENC) {
>>> +      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t
>> *>(ptr);
>>> +    } else if (op == EDP_OP_TYPE_DEC) {
>>> +      ckpt_open_stream_msg_dec_ptr =
>> static_cast<lgs_ckpt_stream_open_t **>(ptr);
>>> +      if (*ckpt_open_stream_msg_dec_ptr == NULL) {
>>> +        *o_err = EDU_ERR_MEM_FAIL;
>>> +        return NCSCC_RC_FAILURE;
>>> +      }
>>> +      memset(*ckpt_open_stream_msg_dec_ptr, '\0',
>> sizeof(lgs_ckpt_stream_open_t));
>>> +      ckpt_open_stream_msg_ptr = *ckpt_open_stream_msg_dec_ptr;
>>> +    } else {
>>> +      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t
>> *>(ptr);
>>>        }
>>> -    memset(*ckpt_open_stream_msg_dec_ptr, '\0',
>> sizeof(lgs_ckpt_stream_open_t));
>>> -    ckpt_open_stream_msg_ptr = *ckpt_open_stream_msg_dec_ptr;
>>> -  } else {
>>> -    ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t
>> *>(ptr);
>>> +
>>> +    rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn,
>> ckpt_open_stream_rec_ed_rules, ckpt_open_stream_msg_ptr,
>>> +                             ptr_data_len, buf_env, op, o_err);
>>>      }
>>>
>>> -  rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn,
>> ckpt_open_stream_rec_ed_rules, ckpt_open_stream_msg_ptr,
>>> -                           ptr_data_len, buf_env, op, o_err);
>>>      return rc;
>>>    }       /* End edp_ed_open_stream_rec */
>>>    /* End of EDU encode/decode functions */
>>> @@ -373,6 +417,18 @@ bool lgs_is_peer_v6() {
>>>    }
>>>
>>>    /**
>>> + * 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.
>>>     *
>>> @@ -598,6 +654,7 @@ uint32_t lgs_ckpt_stream_open_set(log_st
>>>      stream_open->logFileCurrent = const_cast<char *>(logStream-
>>> logFileCurrent.c_str());
>>>      stream_open->fileFmt = logStream->logFileFormat;
>>>      stream_open->logStreamName = const_cast<char *>(logStream-
>>> name.c_str());
>>> +  stream_open->dest_names = const_cast<char *>(logStream-
>>> stb_dest_names.c_str());
>>>      stream_open->maxFileSize = logStream->maxLogFileSize;
>>>      stream_open->maxLogRecordSize = logStream->fixedLogRecordSize;
>>>      stream_open->logFileFullAction = logStream->logFullAction;
>>> @@ -767,6 +824,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_v6_t *data_v6 = NULL;
>>>      lgsv_ckpt_msg_v5_t *data_v5 = NULL;
>>>      lgsv_ckpt_msg_v3_t *data_v3 = NULL;
>>> @@ -779,7 +837,12 @@ static uint32_t ckpt_encode_async_update
>>>
>>>      TRACE_ENTER();
>>>      /* Set reo_hdl from callback arg to ckpt_rec */
>>> -  if (lgs_is_peer_v6()) {
>>> +  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_v6()) {
>>>        data_v6 = reinterpret_cast<lgsv_ckpt_msg_v6_t *>(
>>>            static_cast<long>(cbk_arg->info.encode.io_reo_hdl));
>>>        vdata = data_v6;
>>> @@ -1070,7 +1133,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;
>>> @@ -1092,11 +1159,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;
>>> @@ -1136,6 +1208,8 @@ static uint32_t ckpt_decode_async_update
>>>      lgsv_ckpt_msg_v5_t *ckpt_msg_v5 = &msg_v5;
>>>      lgsv_ckpt_msg_v6_t msg_v6;
>>>      lgsv_ckpt_msg_v6_t *ckpt_msg_v6 = &msg_v6;
>>> +  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;
>>>
>>> @@ -1156,10 +1230,13 @@ static uint32_t ckpt_decode_async_update
>>>
>>>      TRACE_2("\tckpt_rec_type: %d ", (int)hdr_ptr->ckpt_rec_type);
>>>
>>> -  if (lgs_is_peer_v6()) {
>>> +  if (lgs_is_peer_v7()) {
>>> +    ckpt_msg_v7->header = hdr;
>>> +    ckpt_msg = ckpt_msg_v7;
>>> +  } else if (lgs_is_peer_v6()) {
>>>        ckpt_msg_v6->header = hdr;
>>>        ckpt_msg = ckpt_msg_v6;
>>> -  } else   if (lgs_is_peer_v5()) {
>>> +  } else if (lgs_is_peer_v5()) {
>>>        ckpt_msg_v5->header = hdr;
>>>        ckpt_msg = ckpt_msg_v5;
>>>      } else if (lgs_is_peer_v4()) {
>>> @@ -1177,10 +1254,13 @@ 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_v6()) {
>>> +      if (lgs_is_peer_v7()) {
>>> +        reg_rec = &ckpt_msg_v7->ckpt_rec.initialize_client;
>>> +        edp_function_reg = edp_ed_reg_rec_v6;
>>> +      } else if (lgs_is_peer_v6()) {
>>>            reg_rec = &ckpt_msg_v6->ckpt_rec.initialize_client;
>>>            edp_function_reg = edp_ed_reg_rec_v6;
>>> -      }  else if (lgs_is_peer_v5()) {
>>> +      } else if (lgs_is_peer_v5()) {
>>>            reg_rec = &ckpt_msg_v5->ckpt_rec.initialize_client;
>>>            edp_function_reg = edp_ed_reg_rec;
>>>          } else if (lgs_is_peer_v4()) {
>>> @@ -1209,7 +1289,9 @@ static uint32_t ckpt_decode_async_update
>>>
>>>        case LGS_CKPT_OPEN_STREAM: /* 4 */
>>>          TRACE_2("\tSTREAM OPEN: UPDATE");
>>> -      if (lgs_is_peer_v6()) {
>>> +      if (lgs_is_peer_v7()) {
>>> +        stream_open = &ckpt_msg_v7->ckpt_rec.stream_open;
>>> +      } else if (lgs_is_peer_v6()) {
>>>            stream_open = &ckpt_msg_v6->ckpt_rec.stream_open;
>>>          } else if (lgs_is_peer_v5()) {
>>>            stream_open = &ckpt_msg_v5->ckpt_rec.stream_open;
>>> @@ -1467,13 +1549,17 @@ static uint32_t process_ckpt_data(lgs_cb
>>>      lgsv_ckpt_msg_v3_t *data_v3;
>>>      lgsv_ckpt_msg_v5_t *data_v5;
>>>      lgsv_ckpt_msg_v6_t *data_v6;
>>> +  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_v6()) {
>>> +  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_v6()) {
>>>        data_v6 = static_cast<lgsv_ckpt_msg_v6_t *>(data);
>>>        lgsv_ckpt_msg_type = data_v6->header.ckpt_rec_type;
>>>      } else if (lgs_is_peer_v5()) {
>>> @@ -1906,10 +1992,14 @@ 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();
>>>
>>> -  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);
>>> +    param = &data_v7->ckpt_rec.stream_open;
>>> +  } else if (lgs_is_peer_v2()) {
>>>        lgsv_ckpt_msg_v2_t *data_v2 = static_cast<lgsv_ckpt_msg_v2_t
>> *>(data);
>>>        param = &data_v2->ckpt_rec.stream_open;
>>>      } else {
>>> @@ -1974,7 +2064,27 @@ 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);
>>> +
>>> +    // Only update destination names if peer is v7 or upper.
>>> +    if (lgs_is_peer_v7()) {
>>> +      if (param->dest_names != nullptr && strlen(param->dest_names) !=
> 0)
>> {
>>> +        TRACE("Dest_name %s", param->dest_names);
>>> +        stream->stb_dest_names = std::string{param->dest_names};
>>> +        stream->dest_names = logutil::Parser(stream->stb_dest_names,
> ";");
>>> +      } else {
>>> +        stream->stb_dest_names = "";
>>> +        stream->dest_names.clear();
>>> +      }
>>> +    }
>>>      }
>>>
>>>      /* If configured for split file system files shall be opened on
> stand by
>>> @@ -2133,12 +2243,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;
>>> @@ -2191,6 +2317,17 @@ static uint32_t ckpt_proc_cfg_stream(lgs
>>>      strcpy(stream->logFileFormat, logFileFormat);
>>>      stream->severityFilter = severityFilter;
>>>      stream->logFileCurrent = logFileCurrent;
>>> +  // Only update destination name if peer is v7 or upper.
>>> +  if (lgs_is_peer_v7()) {
>>> +    if (dest_names != nullptr && strlen(dest_names) != 0) {
>>> +      TRACE("dest_names: %s", dest_names);
>>> +      stream->stb_dest_names = std::string{dest_names};
>>> +      stream->dest_names = logutil::Parser(stream->stb_dest_names,
> ";");
>>> +    } else {
>>> +      stream->stb_dest_names = "";
>>> +      stream->dest_names.clear();
>>> +    }
>>> +  }
>>>
>>>      /* If split file mode, update standby files */
>>>      if (lgs_is_split_file_system()) {
>>> @@ -2250,7 +2387,10 @@ uint32_t lgs_ckpt_send_async(lgs_cb_t *c
>>>
>>>      TRACE_ENTER();
>>>
>>> -  if (lgs_is_peer_v6()) {
>>> +  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_v6()) {
>>>        lgsv_ckpt_msg_v6_t *ckpt_rec_v6 = static_cast<lgsv_ckpt_msg_v6_t
>> *>(ckpt_rec);
>>>        ckpt_rec_type = ckpt_rec_v6->header.ckpt_rec_type;
>>>      } else if (lgs_is_peer_v5()) {
>>> 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
>>> @@ -40,9 +40,10 @@
>>>    #define LGS_MBCSV_VERSION_4 4
>>>    #define LGS_MBCSV_VERSION_5 5
>>>    #define LGS_MBCSV_VERSION_6 6
>>> +#define LGS_MBCSV_VERSION_7 7
>>>
>>>    /* Current version */
>>> -#define LGS_MBCSV_VERSION 6
>>> +#define LGS_MBCSV_VERSION 7
>>>    #define LGS_MBCSV_VERSION_MIN 1
>>>
>>>    /* Checkpoint message types(Used as 'reotype' w.r.t mbcsv)  */
>>> @@ -98,6 +99,7 @@ typedef struct {
>>>      uint64_t creationTimeStamp;
>>>      uint32_t numOpeners;
>>>      char *logFileCurrent;
>>> +  char *dest_names;
>>>      logStreamTypeT streamType;
>>>      uint32_t logRecordId;   /* log record identifier increased for each
> record
>> */
>>>    } lgs_ckpt_stream_open_t;
>>> @@ -109,6 +111,7 @@ bool lgs_is_peer_v3();
>>>    bool lgs_is_peer_v4();
>>>    bool lgs_is_peer_v5();
>>>    bool lgs_is_peer_v6();
>>> +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_v6, 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,66 @@
>>> +/*      -*- 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"
>>> +#include "log/logd/lgs_mbcsv_v6.h"
>>> +
>>> +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_v6_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_nildest.cc b/src/log/logd/lgs_nildest.cc
>>> new file mode 100644
>>> --- /dev/null
>>> +++ b/src/log/logd/lgs_nildest.cc
>>> @@ -0,0 +1,153 @@
>>> +/*      -*- 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_nildest.h"
>>> +
>>> +#include <algorithm>
>>> +
>>> +#include "base/logtrace.h"
>>> +
>>> +static const char kDelimeter[] = ";";
>>> +
>>> +
>>>
>> +//============================================================
>> ==================
>>> +// NilDestType class
>>>
>> +//============================================================
>> ==================
>>> +NilDestType NilDestType::me_;
>>> +const char NilDestType::name_[] = "NILDEST";
>>> +
>>> +NilDestType::NilDestType() {}
>>> +
>>> +NilDestType::~NilDestType() {
>>> +}
>>> +
>>> +const VectorString NilDestType::GetAllDestStatus() {
>>> +  VectorString output{};
>>> +  if (me_.destnames_.size() == 0) return output;
>>> +  for (const auto& it : me_.destnames_) {
>>> +    std::string status = it + "," + "NOT_CONNECTED";
>>> +    TRACE("%s status = %s", __func__, status.c_str());
>>> +    output.push_back(status);
>>> +  }
>>> +  return output;
>>> +}
>>> +
>>> +bool NilDestType::FindName(const std::string& name) const {
>>> +  return (std::find(destnames_.begin(), destnames_.end(), name) !=
>>> +          destnames_.end());
>>> +}
>>> +
>>> +ErrCode NilDestType::HandleRecordMsg(
>>> +    const DestinationHandler::RecordMsg& msg) {
>>> +  ErrCode ret = ErrCode::kOk;
>>> +  TRACE("Destination name(%s) is NILDEST. Drop msg!", msg.name);
>>> +  ret = ErrCode::kDrop;
>>> +  return ret;
>>> +}
>>> +
>>> +ErrCode NilDestType::HandleCfgMsg(
>>> +    const DestinationHandler::CfgDestMsg& msg) {
>>> +  TRACE_ENTER();
>>> +  ErrCode ret = ErrCode::kOk;
>>> +  const std::string name{msg.name};
>>> +  TRACE("%s Add NILDEST with name (%s)", __func__, msg.name);
>>> +  destnames_.push_back(name);
>>> +  TRACE_LEAVE();
>>> +  return ret;
>>> +}
>>> +
>>> +ErrCode NilDestType::HandleDelCfgMsg(
>>> +    const DestinationHandler::DelDestMsg& msg) {
>>> +  TRACE_ENTER();
>>> +  ErrCode ret = ErrCode::kOk;
>>> +  TRACE("%s remove NILDEST destination name (%s)", __func__,
>> msg.name);
>>> +  const std::string name{msg.name};
>>> +  destnames_.erase(std::remove(destnames_.begin(),
>>> +                               destnames_.end(),
>>> +                               name)
>>> +                   , destnames_.end());
>>> +  TRACE_LEAVE();
>>> +  return ret;
>>> +}
>>> +
>>> +ErrCode NilDestType::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;
>>> +      }
>>> +      destnames_.clear();
>>> +      cfgsent = false;
>>> +      break;
>>> +    }
>>> +
>>> +    default:
>>> +      LOG_ER("Unknown dispatch message type %d",
>> static_cast<int>(msg.type));
>>> +      ret = ErrCode::kInvalid;
>>> +  }
>>> +
>>> +  TRACE_LEAVE();
>>> +  return ret;
>>> +}
>>> diff --git a/src/log/logd/lgs_nildest.h b/src/log/logd/lgs_nildest.h
>>> new file mode 100644
>>> --- /dev/null
>>> +++ b/src/log/logd/lgs_nildest.h
>>> @@ -0,0 +1,72 @@
>>> +/*      -*- 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_NILDEST_H_
>>> +#define SRC_LOG_LOGD_LGS_NILDEST_H_
>>> +
>>> +#include <string>
>>> +
>>> +#include "log/logd/lgs_common.h"
>>> +#include "log/logd/lgs_dest.h"
>>> +
>>> +//>
>>> +// @NilDestType - represent NILDEST destination type
>>> +//
>>> +//<
>>> +class NilDestType {
>>> + public:
>>> +  // @NilDestType 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 @NilDestType. This type must be
>> unique.
>>> +  static const std::string Type() { return std::string{name_}; }
>>> +
>>> + private:
>>> +  NilDestType();
>>> +  ~NilDestType();
>>> +
>>> +  // 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&);
>>> +  // The msg will be droped if sending to @NILDEST type
>>> +  ErrCode HandleRecordMsg(const DestinationHandler::RecordMsg&);
>>> +  // Put the destination name to @destnames_ for querying destination
>> status
>>> +  ErrCode HandleCfgMsg(const DestinationHandler::CfgDestMsg&);
>>> +  // Remove from @destnames_
>>> +  ErrCode HandleDelCfgMsg(const DestinationHandler::DelDestMsg&);
>>> +  // Get all destination status for @NILDEST type
>>> +  // @return a vector of status, with format
>>> +  // {"name,NOT_CONNECTED", "name2,NOT_CONNECTED, etc."}
>>> +  static const VectorString GetAllDestStatus();
>>> +  // Unique object instance for this @NilDestType class
>>> +  static NilDestType& Instance() { return me_; }
>>> +  // Unique instance for this class
>>> +  static NilDestType me_;
>>> +  // The type name of this @NilDestType (my identity).
>>> +  // Must use this "type" name in destination configuration
>>> +  // if want to destinate to @NilDestType
>>> +  static const char name_[];
>>> +
>>> +  // Hold all destination names having @NILDEST type
>>> +  VectorString destnames_;
>>> +
>>> +  DELETE_COPY_AND_MOVE_OPERATORS(NilDestType);
>>> +};
>>> +
>>> +#endif  // SRC_LOG_LOGD_LGS_NILDEST_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"
>>> @@ -376,6 +379,8 @@ void log_stream_print(log_stream_t *stre
>>>      TRACE_2("  logRecordId:          %u", stream->logRecordId);
>>>      TRACE_2("  streamType:           %u", stream->streamType);
>>>      TRACE_2("  filtered:             %llu", stream->filtered);
>>> +  TRACE_2("  stb_dest_names:       %s",
> stream->stb_dest_names.c_str());
>>> +  TRACE_2("  isRtStream:           %d", stream->isRtStream);
>>>    }
>>>
>>>    /**
>>> @@ -480,7 +485,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 +658,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 +1497,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 2008, 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_unixsock_dest.cc
>> b/src/log/logd/lgs_unixsock_dest.cc
>>> new file mode 100644
>>> --- /dev/null
>>> +++ b/src/log/logd/lgs_unixsock_dest.cc
>>> @@ -0,0 +1,343 @@
>>> +/*      -*- 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_unixsock_dest.h"
>>> +
>>> +#include <algorithm>
>>> +
>>> +#include "log/logd/lgs_util.h"
>>> +#include "log/logd/lgs_nildest.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[] = ";";
>>> +
>>>
>> +//============================================================
>> ==================
>>> +// UnixSocketHandler class
>>>
>> +//============================================================
>> ==================
>>> +UnixSocketHandler::UnixSocketHandler(const char* socket_name)
>>> +    : sock_path_{socket_name}
>>> +    , sock_{socket_name}
>>> +    , status_{DestinationStatus::kFailed} {
>>> +      // Open the unix socket & and flush the destination status to
> @status_
>>> +      Open();
>>> +};
>>> +
>>> +void UnixSocketHandler::FlushStatus() {
>>> +  if (sock_.fd() >= 0) {
>>> +    status_ = DestinationStatus::kActive;
>>> +  } else {
>>> +    status_ = DestinationStatus::kFailed;
>>> +  }
>>> +}
>>> +
>>> +DestinationStatus UnixSocketHandler::GetSockStatus() {
>>> +  Open();
>>> +  return status_;
>>> +}
>>> +
>>> +void UnixSocketHandler::Open() {
>>> +  FlushStatus();
>>> +}
>>> +
>>> +void UnixSocketHandler::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 UnixSocketHandler::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 UnixSocketHandler::Close() {
>>> +  // Do nothing
>>> +}
>>> +
>>> +// Convert AIS sev @a sev to Severity class
>>> +base::LogMessage::Severity UnixSocketHandler::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;
>>> +  }
>>> +}
>>> +
>>> +UnixSocketHandler::~UnixSocketHandler() {
>>> +  // Destination is deleted
>>> +  status_ = DestinationStatus::kFailed;
>>> +  // The parent class will do closing the connection
>>> +  // and other resources.
>>> +}
>>> +
>>>
>> +//============================================================
>> ==================
>>> +// UnixSocketType class
>>>
>> +//============================================================
>> ==================
>>> +UnixSocketType UnixSocketType::me_;
>>> +const char UnixSocketType::name_[] = "UNIX_SOCKET";
>>> +
>>> +UnixSocketType::UnixSocketType() {}
>>> +
>>> +UnixSocketType::~UnixSocketType() {
>>> +  // Close all connections & stop the thread
>>> +}
>>> +
>>> +const std::string UnixSocketType::DestStatusToStr(DestinationStatus
>> status) {
>>> +  switch (status) {
>>> +    // Destination configured and is being connected to destination
>>> +    case DestinationStatus::kActive: return "CONNECTED";
>>> +      // Destination configured and is being disconnected to
> destination
>>> +    case DestinationStatus::kFailed: return "FAILED";
>>> +    default: return "UNKNOWN";
>>> +  }
>>> +}
>>> +
>>> +const std::string UnixSocketType::GetDestinationStatus(
>>> +    const std::string& name) {
>>> +  if (FindName(name) == false) return std::string{"UNKNOWN"};
>>> +  const UnixSocketHandler* sk = name_sockethdlr_map_[name];
>>> +  if (sk == nullptr) return std::string{"FAILED"};
>>> +  return DestStatusToStr(name_sockethdlr_map_[name]-
>>> GetSockStatus());
>>> +}
>>> +
>>> +const VectorString UnixSocketType::GetAllDestStatus() {
>>> +  VectorString output{};
>>> +  if (me_.name_sockethdlr_map_.size() == 0) return output;
>>> +  for (const auto& it : me_.name_sockethdlr_map_) {
>>> +    std::string status = it.first + "," +
> me_.GetDestinationStatus(it.first);
>>> +    TRACE("%s status = %s", __func__, status.c_str());
>>> +    output.push_back(status);
>>> +  }
>>> +  return output;
>>> +}
>>> +
>>> +bool UnixSocketType::FindName(const std::string& name) const {
>>> +  return ((name_sockethdlr_map_.find(name) ==
>> name_sockethdlr_map_.end()) ?
>>> +          (false) : (true));
>>> +}
>>> +
>>> +UnixSocketHandler* UnixSocketType::GetDestHandle(const std::string&
>> name) {
>>> +  if (FindName(name) == false) return nullptr;
>>> +  return name_sockethdlr_map_[name];
>>> +}
>>> +
>>> +const char* UnixSocketType::GetCfgValue(const std::string& name) {
>>> +  if (FindName(name) == false) return nullptr;
>>> +  UnixSocketHandler* sk = name_sockethdlr_map_[name];
>>> +  return (sk == nullptr) ? (nullptr) :
>>> +      (name_sockethdlr_map_[name]->sock_path_.c_str());
>>> +}
>>> +
>>> +ErrCode UnixSocketType::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(name_sockethdlr_map_[std::string{msg.name}] != nullptr);
>>> +  return name_sockethdlr_map_[std::string{msg.name}]->Send(msg.rec);
>>> +}
>>> +
>>> +ErrCode UnixSocketType::HandleCfgMsg(
>>> +    const DestinationHandler::CfgDestMsg& msg) {
>>> +  TRACE_ENTER();
>>> +  ErrCode ret = ErrCode::kOk;
>>> +  const std::string name{msg.name};
>>> +  const char* newcfg = msg.value;
>>> +
>>> +  // Close and free previous @LocakSkDestHanlde instance if existing.
>>> +  if (GetDestHandle(name) != nullptr) {
>>> +    TRACE("%s delete old destination value for name (%s)", __func__,
>> msg.name);
>>> +    delete name_sockethdlr_map_[name];
>>> +    name_sockethdlr_map_[name] = nullptr;
>>> +  }
>>> +
>>> +  // Request to create destination handler
>>> +  if ((newcfg != nullptr) && (strlen(newcfg) > 0)) {
>>> +    TRACE("%s add new destination name (%s)", __func__, msg.name);
>>> +    // Create a new destination handler @UnixSocketHandler for @name
>>> +    name_sockethdlr_map_[name] = new UnixSocketHandler(newcfg);
>>> +    ret = (name_sockethdlr_map_[name] == nullptr) ? ErrCode::kNoMem :
>> ret;
>>> +    return ret;
>>> +  } else {
>>> +    TRACE("%s create nildest with destination name (%s)", __func__,
>> msg.name);
>>> +    // nildest case with format "name;type;"
>>> +    name_sockethdlr_map_[name] = nullptr;
>>> +  }
>>> +
>>> +  TRACE_LEAVE();
>>> +  return ret;
>>> +}
>>> +
>>> +ErrCode UnixSocketType::HandleDelCfgMsg(
>>> +    const DestinationHandler::DelDestMsg& msg) {
>>> +  TRACE_ENTER();
>>> +  ErrCode ret = ErrCode::kOk;
>>> +  TRACE("%s remove destination name (%s)", __func__, msg.name);
>>> +  const std::string name{msg.name};
>>> +  // Close and free @LocakSkDestHanlde instance if existing.
>>> +  if (GetDestHandle(name) != nullptr) delete
>> name_sockethdlr_map_[name];
>>> +  // Erase from the map
>>> +  name_sockethdlr_map_.erase(name);
>>> +  TRACE_LEAVE();
>>> +  return ret;
>>> +}
>>> +
>>> +ErrCode UnixSocketType::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 : name_sockethdlr_map_) {
>>> +        if (it.second != nullptr) delete it.second;
>>> +        name_sockethdlr_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;
>>> +}
>>> diff --git a/src/log/logd/lgs_unixsock_dest.h
>> b/src/log/logd/lgs_unixsock_dest.h
>>> new file mode 100644
>>> --- /dev/null
>>> +++ b/src/log/logd/lgs_unixsock_dest.h
>>> @@ -0,0 +1,245 @@
>>> +/*      -*- 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_UNIXSOCK_DEST_H_
>>> +#define SRC_LOG_LOGD_LGS_UNIXSOCK_DEST_H_
>>> +
>>> +#include <string>
>>> +#include <map>
>>> +
>>> +#include "base/log_message.h"
>>> +#include "base/time.h"
>>> +#include "base/unix_client_socket.h"
>>> +#include "log/logd/lgs_common.h"
>>> +#include "log/logd/lgs_dest.h"
>>> +
>>> +//>
>>> +// Represent connection to local Unix Domain socket
>>> +// @UnixSocketHandler = 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 @UnixSocketType 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 UnixSocketHandler {
>>> + 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 UnixSocketType.
>>> +  friend class UnixSocketType;
>>> +  // 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 UnixSocketHandler(const char*);
>>> +  ~UnixSocketHandler();
>>> +
>>> +  // 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(UnixSocketHandler);
>>> +};
>>> +
>>> +//>
>>> +// @UnixSocketType: 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 @UnixSocketHandler
>> 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 @UnixSocketHandler instance is mapped to that
>>> +//       destination name?
>>> +//    If no destination name exist in the @map, then Hander will
>>> +//    create a new pair <name, @UnixSocketHandler> 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 @UnixSocketHandler instance is mapped to that
>>> +//       destination name?
>>> +//    With this message, the Handler expects the destination name and
>>> +//    @UnixSocketHandler 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
>>> +//    @UnixSocketHandler 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 UnixSocketType {
>>> + public:
>>> +  // @UnixSocketType 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 @UnixSocketType. 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 NameSockHdlrMap =  std::map<std::string, UnixSocketHandler*>;
>>> +
>>> +  UnixSocketType();
>>> +  ~UnixSocketType();
>>> +
>>> +  // Get @UnixSocketHandler instance which has been assigned
>>> +  // to this destination @name
>>> +  UnixSocketHandler* 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
>>> +  // @UnixSocketHandler 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) "CONNECTED": destination @name is configured and connected to
>> other end.
>>> +  // 2) "FAILED": destination @name is configured, but not able co
> connect
>> to.
>>> +  //    or no destination value has been given.
>>> +  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 @UnixSocketType class
>>> +  static UnixSocketType& Instance() { return me_; }
>>> +  // Unique instance for this class
>>> +  static UnixSocketType me_;
>>> +  // The type name of this @UnixSocketType (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 @UnixSocketHandler instance.
>>> +  // Same @name must have same @UnixSocketHandler instance.
>>> +  NameSockHdlrMap name_sockethdlr_map_;
>>> +
>>> +  DELETE_COPY_AND_MOVE_OPERATORS(UnixSocketType);
>>> +};
>>> +
>>> +#endif  // SRC_LOG_LOGD_LGS_UNIXSOCK_DEST_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
>>> @@ -837,6 +837,40 @@ 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 isValidName(const std::string& name) {
>>> +  // Valid name if @name not contain any characters outside
>>> +  // of below strings.
>>> +  const std::string validChar = "abcdefghijklmnopqrstuvwxyz"
>>> +                                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
>>> +                                "01234567890_-";
>>> +  if (name.find_first_not_of(validChar) != std::string::npos)
>>> +    return false;
>>> +
>>> +  return true;
>>> +}
>>> +
>>> +};  // namespace logutil
>>> +
>>>    /**
>>>     * Check if severity filter is supported for client
>>>     *
>>> 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
>>> @@ -27,7 +27,7 @@
>>>
>>>    #include "osaf/saf/saAis.h"
>>>    #include "amf/saf/saAmf.h"
>>> -
>>> +#include <vector>
>>>    #include "lgs_stream.h"
>>>    #include "lgs_evt.h"
>>>
>>> @@ -86,4 +86,13 @@ bool lgs_is_extended_name_valid(const Sa
>>>    void lgs_send_severity_filter_to_clients(uint32_t stream_id,
>>>                                             SaLogSeverityFlagsT
> serverity);
>>> +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 isValidName(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
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to