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