Hi Vu Ack with commants See comments below [Lennart]
Thanks Lennart > -----Original Message----- > From: Vu Minh Nguyen [mailto:vu.m.ngu...@dektech.com.au] > Sent: den 21 februari 2017 10:34 > To: Lennart Lund <lennart.l...@ericsson.com>; mahesh.va...@oracle.com; > Canh Van Truong <canh.v.tru...@dektech.com.au> > Cc: opensaf-devel@lists.sourceforge.net > Subject: [PATCH 1 of 3] log: add alternative destinations of log records > [#2258] > > src/log/Makefile.am | 8 +- > src/log/config/logsv_classes.xml | 7 +- > src/log/logd/lgs_config.cc | 169 ++++++++- > src/log/logd/lgs_config.h | 3 +- > src/log/logd/lgs_dest.cc | 707 > +++++++++++++++++++++++++++++++++++++++ > src/log/logd/lgs_dest.h | 576 +++++++++++++++++++++++++++++++ > src/log/logd/lgs_evt.cc | 33 + > src/log/logd/lgs_imm.cc | 202 +++++++++- > src/log/logd/lgs_main.cc | 8 + > src/log/logd/lgs_mbcsv.cc | 103 +++++- > src/log/logd/lgs_mbcsv.h | 6 +- > src/log/logd/lgs_mbcsv_v5.cc | 10 + > src/log/logd/lgs_mbcsv_v7.cc | 177 +++++++++ > src/log/logd/lgs_mbcsv_v7.h | 67 +++ > src/log/logd/lgs_stream.cc | 60 +++- > src/log/logd/lgs_stream.h | 16 + > src/log/logd/lgs_util.cc | 63 +++ > src/log/logd/lgs_util.h | 11 +- > 18 files changed, 2154 insertions(+), 72 deletions(-) > > > Here are major info, detailed info will be added to PR doc soon. > 1) Add attribute "saLogRecordDestination" to log stream. > 2) Add Local socket destintion handler > 3) Integrate into first increment made by Lennart > > diff --git a/src/log/Makefile.am b/src/log/Makefile.am > --- a/src/log/Makefile.am > +++ b/src/log/Makefile.am > @@ -72,9 +72,11 @@ noinst_HEADERS += \ > src/log/logd/lgs_mbcsv_v2.h \ > src/log/logd/lgs_mbcsv_v3.h \ > src/log/logd/lgs_mbcsv_v5.h \ > + src/log/logd/lgs_mbcsv_v7.h \ > src/log/logd/lgs_recov.h \ > src/log/logd/lgs_stream.h \ > - src/log/logd/lgs_util.h > + src/log/logd/lgs_util.h \ > + src/log/logd/lgs_dest.h > > bin_PROGRAMS += bin/saflogger > osaf_execbin_PROGRAMS += bin/osaflogd > @@ -118,10 +120,12 @@ bin_osaflogd_SOURCES = \ > src/log/logd/lgs_mbcsv_v2.cc \ > src/log/logd/lgs_mbcsv_v3.cc \ > src/log/logd/lgs_mbcsv_v5.cc \ > + src/log/logd/lgs_mbcsv_v7.cc \ > src/log/logd/lgs_mds.cc \ > src/log/logd/lgs_recov.cc \ > src/log/logd/lgs_stream.cc \ > - src/log/logd/lgs_util.cc > + src/log/logd/lgs_util.cc \ > + src/log/logd/lgs_dest.cc > > bin_osaflogd_LDADD = \ > lib/libosaf_common.la \ > diff --git a/src/log/config/logsv_classes.xml > b/src/log/config/logsv_classes.xml > --- a/src/log/config/logsv_classes.xml > +++ b/src/log/config/logsv_classes.xml > @@ -147,12 +147,13 @@ > <category>SA_CONFIG</category> > <flag>SA_WRITABLE</flag> > </attr> > - <attr> > + <attr> > <name>saLogRecordDestination</name> > - <type>SA_UINT32_T</type> > + <type>SA_STRING_T</type> > <category>SA_CONFIG</category> > <flag>SA_WRITABLE</flag> > - <flag>SA_MULTI_VALUE</flag> > + <flag>SA_MULTI_VALUE</flag> > + <flag>SA_NO_DUPLICATES</flag> > </attr> > <attr> > <name>saLogStreamCreationTimestamp</name> > diff --git a/src/log/logd/lgs_config.cc b/src/log/logd/lgs_config.cc > --- a/src/log/logd/lgs_config.cc > +++ b/src/log/logd/lgs_config.cc > @@ -40,6 +40,7 @@ > #include "osaf/immutil/immutil.h" > #include "log/logd/lgs_file.h" > #include "log/logd/lgs.h" > +#include "log/logd/lgs_util.h" [Lennart] This include should be removed, not needed > > static SaVersionT immVersion = { 'A', 2, 11 }; > > @@ -709,33 +710,162 @@ static int lgs_cfg_verify_log_filesys_co > return rc; > } > > +//> > +// Utility functions to validate destination configuration format > +//< > +// Typedef for shorten declaration > +// This type could be used to hold set of strings with formats > +// 1) {"name;type;value", etc.} > +// 2) {"name", "type", "value"} > +// 3) etc. > +using VectorString = std::vector<std::string>; > +//> > +// Define the "token" possition in destination configuration. > +// kName : use this index to get "name" token > +// kType : use this index to get "type" token > +// kValue: use this index to get "value" token > +// kSize : this is the maximum size of the dest configuration vector. > +//< > +enum { kName = 0, kType, kValue, kSize }; > +// Tokens seperator > +const char kSemicolon[] = ";"; > + > + > +// The format of destination must be one of followings: > +// 1) "name;type;" > +// 2) "name;type;value" > +// So, in destination configuration, must have 02 semiconlons > +// no more, no less. > +// > +bool is_right_destination_fmt(const VectorString& vdest) { > + int nl_cnt = 0; > + // Check each single destination > + for (const auto& it : vdest) { > + nl_cnt = std::count(it.begin(), it.end(), ';'); > + if (nl_cnt != 2) { > + TRACE("%s wrong destination format", __func__); > + return false; > + } > + } > + return true; > +} > + > +// Return false if "name" token is invalid > +bool is_name_valid(const VectorString& vdest) { > + // Check each single destination > + for (const auto& it : vdest) { > + const VectorString sdes = logutil::Parser(it, kSemicolon); > + if (sdes[kName].length() == 0) { > + TRACE("%s wrong name format (name is empty value)", __func__); > + return false; > + } > + // invalid name if having special characters in > + if (logutil::isUnixName(sdes[kName]) == false) { > + TRACE("%s wrong name format (has special chars in)", __func__); > + return false; > + } > + } > + return true; > +} > + > +class LocalSocketType; > + > +// Return false if "type" token is invalid > +bool is_type_valid(const VectorString& vdest) { > + // Check each single destination > + for (const auto& it : vdest) { > + const VectorString sdes = logutil::Parser(it, kSemicolon); > + if (sdes[kType].length() == 0) { > + TRACE("%s wrong type format (type is empty value)", __func__); > + return false; > + } > + // Only support type = "localsocket" > + if (sdes[kType] != "localsocket") { > + TRACE("%s wrong type format (only support type = localsocket)", > __func__); > + return false; > + } > + } > + return true; > +} > + > +// Return true if no dulicated info, otherwise false. > +bool is_no_config_duplicated(const VectorString& vdest, > + const VectorString& vdes2) { > + // Compare values of each pair of destinations. > + for (const auto& it : vdest) { > + const VectorString sdes = logutil::Parser(it, kSemicolon); > + for (const auto& it2 : vdes2) { > + if (it == it2) continue; > + const VectorString sdes2 = logutil::Parser(it2, kSemicolon); > + // Duplicate name > + if (sdes2[kName] == sdes[kName]) { > + TRACE("%s name is duplicated", __func__); > + return false; > + } > + // Ignore nildest > + if ((sdes[kValue].length() == 0) || (sdes2[kValue].length() == 0)) > + continue; > + // Duplicate "value" > + if (sdes2[kValue] == sdes[kValue]) { > + TRACE("%s value is duplicated", __func__); > + return false; > + } > + } > + } > + return true; > +} > + > +// Check whether there is duplicated "name" or "value" > +// in provided @vdest and existing destinations > +bool check_configuration_duplicated(const VectorString& vdest, > + SaImmAttrModificationTypeT type) { > + if (type == SA_IMM_ATTR_VALUES_DELETE) return true; > + // No checking duplicated if replacing to one value > + if (type == SA_IMM_ATTR_VALUES_REPLACE && vdest.size() < 2) return > true; > + // Check in all replace values if any duplicated name/value > + if (type == SA_IMM_ATTR_VALUES_REPLACE) { > + // Check whethere there is duplicated "name" or "value" > + // in list of provided destination configurations. > + return is_no_config_duplicated(vdest, vdest); > + } else { > + bool isNoDuplicated = true; > + // Check whethere there is duplicated "name" or "value" > + // in adding destination configurations and existing ones. > + // Firstly, check if any duplicate in added items. > + if (vdest.size() > 1) { > + isNoDuplicated = is_no_config_duplicated(vdest, vdest); > + if (isNoDuplicated == false) return false; > + } > + // Secondly, check if any duplicate in added items with existing list. > + return is_no_config_duplicated( > + vdest, lgs_conf.logRecordDestinationConfiguration); > + } > +} > + > /** > * Verify all values of log_record_destination_configuration > * Rules: > * - Empty string is Ok else > - * - String shall have at least three fields separated by '\n' > - * - First and second field cannot be empty > + * - The destination configiration must be in right format > + * - "name" token must be valid > + * - "type" token must be valid > + * - Must no duplicated info > * > * @param log_record_destination_configuration[in] > * @return -1 on error > */ > int lgs_cfg_verify_log_record_destination_configuration( > - std::vector<std::string>& log_record_destination_configuration) { > - int rc = 0; > - TRACE_ENTER(); > - > - int nl_cnt = 0; > - for (auto& config : log_record_destination_configuration) { > - // Verify that the string contains at least 2 ';' > - nl_cnt = std::count(config.begin(), config.end(), ';'); > - if (nl_cnt < 2) { > - rc = -1; > - break; > - } > - } > - > - TRACE_LEAVE2("rc = %s", rc == -1? "Fail": "Pass"); > - return rc; > + std::vector<std::string>& vdest, > + SaImmAttrModificationTypeT type) { > + // Allow deleting all destinations. > + if (vdest.size() == 0) return true; > + // It is important to keep the check in order > + bool result = (is_right_destination_fmt(vdest) && > + is_name_valid(vdest) && > + is_type_valid(vdest) && > + check_configuration_duplicated(vdest, type) > + ); > + return (result == true) ? (0) : (-1); > } > > > @@ -846,7 +976,8 @@ static int verify_all_init() { > } > > if (lgs_cfg_verify_log_record_destination_configuration( > - lgs_conf.logRecordDestinationConfiguration) == -1) { > + lgs_conf.logRecordDestinationConfiguration, > + SA_IMM_ATTR_VALUES_ADD) == -1) { > lgs_conf.logRecordDestinationConfiguration.clear(); > lgs_conf.logRecordDestinationConfiguration_cnfflag = LGS_CNF_DEF; > rc = -1; > diff --git a/src/log/logd/lgs_config.h b/src/log/logd/lgs_config.h > --- a/src/log/logd/lgs_config.h > +++ b/src/log/logd/lgs_config.h > @@ -304,7 +304,8 @@ int lgs_cfg_verify_mbox_limit(uint32_t h > int lgs_cfg_verify_max_application_streams(uint32_t max_app_streams); > int lgs_cfg_verify_file_io_timeout(uint32_t log_file_io_timeout); > int lgs_cfg_verify_log_record_destination_configuration( > - std::vector<std::string>& > log_record_destination_configuration); > + std::vector<std::string>& vdest, > + SaImmAttrModificationTypeT type); > /* > * Functions for updating some parameters. Used to support check-point > before > * version 5 > diff --git a/src/log/logd/lgs_dest.cc b/src/log/logd/lgs_dest.cc > new file mode 100644 > --- /dev/null > +++ b/src/log/logd/lgs_dest.cc > @@ -0,0 +1,707 @@ > +/* -*- OpenSAF -*- > + * > + * Copyright Ericsson AB 2017 - All Rights Reserved. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are > licensed > + * under the GNU Lesser General Public License Version 2.1, February 1999. > + * The complete license can be accessed from the following location: > + * http://opensource.org/licenses/lgpl-license.php > + * See the Copying file included with the OpenSAF distribution for full > + * licensing terms. > + * > + * Author(s): Ericsson AB > + * > + */ > + > +#include "log/logd/lgs_dest.h" > + > +#include <algorithm> > + > +#include "log/logd/lgs_util.h" > +#include "log/logd/lgs_config.h" > +#include "log/saf/saLog.h" > +#include "base/logtrace.h" > +// RFC5424 syslog msg format > +// ========================= > +// 1) HEADER > +// PRI = 16*8 + SaLogSeverityT > +// VERSION = 1 > +// TIMESTAMP = When a log record was actually logged > +// E.g: 2016-12-02T11:34:15.944364+01:00 > +// HOSTNAME = FQDN > +// APP-NAME = App name who generated log record > +// PROCID = NILVALUE > +// MSGID = A hash generated from log stream DN > +// STRUCTURED-DATA = NILVALUE > +// > +// 2) MSG BODY > +// MSG = The log record written to local file > +// > + > +static const char kDelimeter[] = ";"; > + > +//======================================================== > ====================== > +// LocalSocketHandler class > +//======================================================== > ====================== > +LocalSocketHandler::LocalSocketHandler(const char* socket_name) > + : sock_path_{socket_name} > + , sock_{socket_name} > + , status_{DestinationStatus::kInactive} { > + // Open the unix socket & and flush the destination status to @status_ > + Open(); > +}; > + > +void LocalSocketHandler::FlushStatus() { > + if (sock_.fd() >= 0) { > + status_ = DestinationStatus::kActive; > + } else { > + status_ = DestinationStatus::kFailed; > + } > +} > + > +DestinationStatus LocalSocketHandler::GetSockStatus() { > + Open(); > + return status_; > +} > + > +void LocalSocketHandler::Open() { > + FlushStatus(); > +} > + > +void LocalSocketHandler::FormRfc5424( > + const DestinationHandler::RecordInfo& msg, > + RfcBuffer* buf) { > + base::LogMessage::Severity sev{Sev(msg.severity)}; > + base::LogMessage::HostName hostname{msg.origin}; > + base::LogMessage::ProcId procid{""}; > + base::LogMessage::AppName appname{msg.app_name}; > + > + base::LogMessage::Write(base::LogMessage::Facility::kLocal0, > + sev, > + msg.time, > + hostname, > + appname, > + procid, > + base::LogMessage::MsgId{msg.msgid}, > + {}, > + std::string{msg.log_record}, > + buf); > +} > + > +ErrCode LocalSocketHandler::Send(const DestinationHandler::RecordInfo& > msg) { > + TRACE_ENTER(); > + RfcBuffer buffer; > + > + FormRfc5424(msg, &buffer); > + > + ssize_t length = buffer.size(); > + ssize_t len = sock_.Send(buffer.data(), length); > + // Resend as probably receiver has just been restarted. > + // Retry only once, if not succesfully, leave it to upper layer decision. > + if (len != length) { > + LOG_NO("Failed to send log record to socket destination. Resent."); > + len = sock_.Send(buffer.data(), length); > + } > + > + FlushStatus(); > + > + TRACE_LEAVE(); > + return ((len != length) ? (ErrCode::kErr) : (ErrCode::kOk)); > +} > + > +void LocalSocketHandler::Close() { > + // Do nothing > +} > + > +// Convert AIS sev @a sev to Severity class > +base::LogMessage::Severity LocalSocketHandler::Sev(uint16_t sev) { > + switch (sev) { > + case SA_LOG_SEV_EMERGENCY: > + return base::LogMessage::Severity::kEmerg; > + case SA_LOG_SEV_ALERT: > + return base::LogMessage::Severity::kAlert; > + case SA_LOG_SEV_CRITICAL: > + return base::LogMessage::Severity::kCrit; > + case SA_LOG_SEV_ERROR: > + return base::LogMessage::Severity::kErr; > + case SA_LOG_SEV_WARNING: > + return base::LogMessage::Severity::kWarning; > + case SA_LOG_SEV_NOTICE: > + return base::LogMessage::Severity::kNotice; > + case SA_LOG_SEV_INFO: > + return base::LogMessage::Severity::kInfo; > + default: > + LOG_ER("Unknown severity level (%d)", (int)sev); > + return base::LogMessage::Severity::kErr; > + } > +} > + > +LocalSocketHandler::~LocalSocketHandler() { > + // Destination is deleted > + status_ = DestinationStatus::kInactive; > + // The parent class will do closing the connection > + // and other resources. > +} > + > +//======================================================== > ====================== > +// LocalSocketType class > +//======================================================== > ====================== > +LocalSocketType LocalSocketType::me_; > +const char LocalSocketType::name_[] = "localsocket"; > + > +LocalSocketType::LocalSocketType() {} > + > +LocalSocketType::~LocalSocketType() { > + // Close all connections & stop the thread > +} > + > +const std::string LocalSocketType::DestStatusToStr(DestinationStatus > status) { > + switch (status) { > + // Destination configured and is being connected to destination > + case DestinationStatus::kActive: return "active"; > + // Destination configured and is being disconnected to destination > + case DestinationStatus::kFailed: return "failed"; > + // Destination is not configured (nildest) > + case DestinationStatus::kInactive: return "inactive"; > + default: return "unknown"; > + } > +} > + > +const std::string LocalSocketType::GetDestinationStatus( > + const std::string& name) { > + if (FindName(name) == false) return std::string{"inactive"}; > + const LocalSocketHandler* sk = nametype_map_[name]; > + if (sk == nullptr) return std::string{"inactive"}; > + return DestStatusToStr(nametype_map_[name]->GetSockStatus()); > +} > + > +const VectorString LocalSocketType::GetAllDestStatus() { > + VectorString output{}; > + if (me_.nametype_map_.size() == 0) return output; > + for (const auto& it : me_.nametype_map_) { > + std::string status = it.first + "," + me_.GetDestinationStatus(it.first); > + output.push_back(status); > + } > + return output; > +} > + > +bool LocalSocketType::FindName(const std::string& name) const { > + return ((nametype_map_.find(name) == nametype_map_.end()) ? > + (false) : (true)); > +} > + > +LocalSocketHandler* LocalSocketType::GetDestHandle(const std::string& > name) { > + if (FindName(name) == false) return nullptr; > + return nametype_map_[name]; > +} > + > +const char* LocalSocketType::GetCfgValue(const std::string& name) { > + if (FindName(name) == false) return nullptr; > + LocalSocketHandler* sk = nametype_map_[name]; > + return (sk == nullptr) ? nullptr : nametype_map_[name]- > >sock_path_.c_str(); > +} > + > +ErrCode LocalSocketType::HandleRecordMsg( > + const DestinationHandler::RecordMsg& msg) { > + ErrCode ret = ErrCode::kOk; > + // No destination configuration with this destination name > + if (FindName(std::string{msg.name}) == false) { > + LOG_WA("No such destination name(%s) configured", msg.name); > + ret = ErrCode::kDrop; > + return ret; > + } > + > + // NIL destination - only have "name" and "type", but no "value". > + if (GetDestHandle(std::string{msg.name}) == nullptr) { > + LOG_NO("The destination name(%s) not yet configured", msg.name); > + ret = ErrCode::kDrop; > + return ret; > + } > + > + // Send to destination end. > + osafassert(nametype_map_[std::string{msg.name}] != nullptr); > + return nametype_map_[std::string{msg.name}]->Send(msg.rec); > +} > + > +ErrCode LocalSocketType::HandleCfgMsg( > + const DestinationHandler::CfgDestMsg& msg) { > + TRACE_ENTER(); > + ErrCode ret = ErrCode::kOk; > + const std::string name{msg.name}; > + const char* newcfg = msg.value; > + > + // Request to create destination handler > + if ((newcfg != nullptr) && (strlen(newcfg) > 0)) { > + // Close and free previous @LocakSkDestHanlde instance if existing. > + if (GetDestHandle(name) != nullptr) delete nametype_map_[name]; > + // Create a new destination handler @LocalSocketHandler for @name > + nametype_map_[name] = new LocalSocketHandler(newcfg); > + ret = (nametype_map_[name] == nullptr) ? ErrCode::kNoMem : ret; > + return ret; > + } > + > + // Request to delete the existing destination handler (NILDEST case) > + if (GetDestHandle(name) != nullptr) { > + delete nametype_map_[name]; > + nametype_map_[name] = nullptr; > + } > + > + TRACE_LEAVE(); > + return ret; > +} > + > +ErrCode LocalSocketType::HandleDelCfgMsg( > + const DestinationHandler::DelDestMsg& msg) { > + TRACE_ENTER(); > + ErrCode ret = ErrCode::kOk; > + > + const std::string name{msg.name}; > + // Close and free @LocakSkDestHanlde instance if existing. > + if (GetDestHandle(name) != nullptr) delete nametype_map_[name]; > + // Erase from the map > + nametype_map_.erase(name); > + > + TRACE_LEAVE(); > + return ret; > +} > + > +ErrCode LocalSocketType::ProcessMsg( > + const DestinationHandler::HandleMsg& msg) { > + TRACE_ENTER(); > + ErrCode ret = ErrCode::kOk; > + static bool cfgsent = false; > + // The proper sequence should be: > + // 1) kCfgDest : for configure destination handlers > + // 2) kSendRecord: write log record to destination > + // 3) kDelDest : delete specific destinations > + // 4) kNoDest : delete all destinations > + // Msg could be drop if the sequence is not correct. > + switch (msg.type) { > + case DestinationHandler::MsgType::kSendRecord: { > + // No destination yet configured. Drop the message. > + TRACE("%s %s", __func__, "RecordMsg msg"); > + const DestinationHandler::RecordMsg& rmsg = msg.info.rec; > + > + // No destination has been configured so far. > + if (cfgsent == false) { > + LOG_WA("Destination handler not yet initialized. Drop msg."); > + ret = ErrCode::kDrop; > + } else { > + ret = HandleRecordMsg(rmsg); > + } > + break; > + } > + > + case DestinationHandler::MsgType::kCfgDest: { > + const DestinationHandler::CfgDestMsg& cmsg = msg.info.cfg; > + TRACE("%s %s", __func__, "CfgDestMsg msg"); > + ret = HandleCfgMsg(cmsg); > + // Notice there at leat one destination configured. > + if (ret == ErrCode::kOk) cfgsent = true; > + break; > + } > + > + case DestinationHandler::MsgType::kDelDest: { > + const DestinationHandler::DelDestMsg& dmsg = msg.info.del; > + TRACE("%s %s", __func__, "DelDestMsg msg"); > + // No destination yet configured. Drop the message. > + if (cfgsent == false) { > + LOG_NO("No destination configured yet. Drop msg."); > + return ErrCode::kDrop; > + } > + ret = HandleDelCfgMsg(dmsg); > + break; > + } > + > + case DestinationHandler::MsgType::kNoDest: { > + LOG_NO("%s %s", __func__, "DelAllCfgDest msg"); > + if (cfgsent == false) { > + LOG_NO("No destination configured yet"); > + return ErrCode::kDrop; > + } > + > + for (auto& it : nametype_map_) { > + if (it.second != nullptr) delete it.second; > + nametype_map_.erase(it.first); > + } > + cfgsent = false; > + break; > + } > + > + default: > + LOG_ER("Unknown dispatch message type %d", > static_cast<int>(msg.type)); > + ret = ErrCode::kInvalid; > + } > + > + TRACE_LEAVE(); > + return ret; > +} > + > +//======================================================== > ====================== > +// DestinationHandler class > +//======================================================== > ====================== > +DestinationHandler DestinationHandler::me_; > + > +// Only support "kUnix" so far > +DestinationHandler::DestType DestinationHandler::GetDestType( > + const std::string& type) { > + if (type == LocalSocketType::Type()) { > + return DestType::kUnix; > + } else { > + LOG_NO("Unknown destination type %s", type.c_str()); > + return DestType::kUnknown; > + } > +} > + > +std::string DestinationHandler::GenerateMsgId( > + const std::string& dn, bool isRtStream) { > + const std::string parent = ",safApp=safLogService"; > + std::string msgid{""}; > + size_t posa = 0, posb = 0; > + > + // Extract stream name from stream DN. > + posa = dn.find('='); > + if ((posb = dn.find(',')) == std::string::npos) { > + posb = dn.length(); > + } > + std::string sname = dn.substr(posa + 1, posb - posa - 1); > + > + //> > + // Rules to generate msgid string: > + // 1) Use stream name (e.g: saLogSystem) + 'C'/'R' if it is not over 31 > chars > + // and the stream DN contains the @parent ",safApp=safLogService"; > + // Note: 'C' means configuration stream, 'R' means runtime stream. > + // Why 31? It is due to MSGID field length limitation > + // (max is 32 chars length). > + // 2) Otherwise, generate a hash number from stream DN > + // if stream name is over 31 chars. > + //< > + if (sname.length() < 32 && dn.find(parent) != std::string::npos) { > + msgid = ((isRtStream == true) ? std::string{sname + 'R'} : > + std::string{sname + 'C'}); > + } else { > + msgid = std::to_string(std::hash<std::string>{}(dn)); > + } > + > + return msgid; > +} > + > +DestinationHandler::DestType DestinationHandler::ToDestType( > + const std::string& name) { > + if (nametype_map_.find(name) == nametype_map_.end()) > + return DestType::kUnknown; > + return nametype_map_[name]; > +} > + > +void DestinationHandler::UpdateDestTypeDb( > + const DestinationHandler::HandleMsg& msg) { > + MsgType mtype = msg.type; > + > + switch (mtype) { > + case MsgType::kCfgDest: { > + const CfgDestMsg& cdest = msg.info.cfg; > + TRACE("%s kCfgDest (%s)", __func__, cdest.name); > + nametype_map_[std::string{cdest.name}] = > + GetDestType(std::string{cdest.type}); > + break; > + } > + > + case MsgType::kDelDest: { > + const DelDestMsg& deldest = msg.info.del; > + TRACE("%s kDelDest (%s)", __func__, deldest.name); > + nametype_map_.erase(std::string{deldest.name}); > + break; > + } > + > + case MsgType::kNoDest: { > + TRACE("%s kNoDest", __func__); > + for (auto& it : nametype_map_) nametype_map_.erase(it.first); > + break; > + } > + > + default: > + LOG_WA("Unknown Dispatch message type (%d)", > static_cast<int>(mtype)); > + break; > + } > +} > + > +ErrCode DestinationHandler::DispatchTo( > + const DestinationHandler::HandleMsg& msg, > + const std::string& name) { > + ErrCode rc = ErrCode::kOk; > + MsgType mtype = msg.type; > + // All destinations has been deleted. Don't care of destination types. > + // Delete all existing connections of all types. > + if (mtype == MsgType::kNoDest) { > + rc = LocalSocketType::Instance().ProcessMsg(msg); > + // Update the destination names from @DestTypeDb > + UpdateDestTypeDb(msg); > + UpdateRtDestStatus(); > + return rc; > + } > + > + // Update the @DestTypeDb first before processing the > DestinationHandler msg. > + // Otherwise, finding destination @type will result nothing. > + // Then, the msg will be drop as no destination type found. > + // But with @MsgType::kDelDest, updating should be last > + // after fowarding the msg to @LocalSocketType. > + // Otherwise, resource might be leaked (e.g: connection is not closed) > + if (mtype == MsgType::kCfgDest) { > + UpdateDestTypeDb(msg); > + } > + > + DestType destType = ToDestType(name); > + switch (destType) { > + // Pass the DestinationHandler msg to Local Unix Domain > + // socket destination type for further processing. > + case DestType::kUnix: > + rc = LocalSocketType::Instance().ProcessMsg(msg); > + break; > + > + default: > + LOG_ER("Not support this destination type(%d), or no dest configured!", > + static_cast<int>(destType)); > + rc = ErrCode::kDrop; > + break; > + } > + > + // Delete destination names from @DestTypeDb > + if (mtype == MsgType::kDelDest) { > + UpdateDestTypeDb(msg); > + } > + > + UpdateRtDestStatus(); > + return rc; > +} > + > +// Update the `logRecordDestinationStatus` here whenever > +// getting CfgDestination or WriteToDestination (?) > +void DestinationHandler::UpdateRtDestStatus() { > + VectorString vstatus = GetDestinationStatus(); > + lgs_config_chg_t config_data = {NULL, 0}; > + lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_STATUS, > vstatus, &config_data); > + // Update configuration data store, no need to checkpoint the status to > stb. > + int ret = lgs_cfg_update(&config_data); > + if (ret == -1) { > + LOG_WA("%s lgs_cfg_update Fail", __func__); > + } > +} > + > +void DestinationHandler::FormCfgDestMsg( > + const std::string& dest, CfgDestMsg* msg) { > + osafassert(msg != nullptr); > + const VectorString tmp = logutil::Parser(dest, kDelimeter); > + strncpy(msg->name, tmp[kName].c_str(), kMaxChar); > + if (tmp.size() > 1) { > + strncpy(msg->type, tmp[kType].c_str(), kMaxChar); > + } > + if (tmp.size() == kSize) > + strncpy(msg->value, tmp[kValue].c_str(), kMaxChar); > +} > + > +void DestinationHandler::FormDelDestMsg( > + const std::string& dest, DelDestMsg* msg) { > + osafassert(msg != nullptr); > + const VectorString tmp = logutil::Parser(dest, kDelimeter); > + strncpy(msg->name, tmp[kName].c_str(), kMaxChar); > +} > + > +bool DestinationHandler::VectorFind( > + const VectorString& vec, const std::string name) { > + return std::find(vec.begin(), vec.end(), name) != vec.end(); > +} > + > +ErrCode DestinationHandler::AddDestConfig(const VectorString& vdest) { > + TRACE_ENTER(); > + ErrCode ret = ErrCode::kOk; > + CfgDestMsg cmsg; > + // Go thought all destinations and configure one by one > + for (unsigned i = 0; i < vdest.size(); i++) { > + HandleMsg msg{}; > + FormCfgDestMsg(vdest[i], &cmsg); > + msg.type = MsgType::kCfgDest; > + memcpy(&msg.info.cfg, &cmsg, sizeof(cmsg)); > + ret = DispatchTo(msg, cmsg.name); > + allcfgdests_map_.push_back(vdest[i]); > + } > + TRACE_LEAVE(); > + return ret; > +} > + > +ErrCode DestinationHandler::DelDestConfig(const VectorString& vdest) { > + TRACE_ENTER(); > + ErrCode ret = ErrCode::kOk; > + DelDestMsg dmsg; > + // Go thought all destinations and configure one by one > + for (unsigned i = 0; i < vdest.size(); i++) { > + if (VectorFind(allcfgdests_map_, vdest[i]) == false) { > + LOG_NO("Not have such dest configuration (%s)", vdest[i].c_str()); > + ret = ErrCode::kDrop; > + continue; > + } > + > + HandleMsg msg{}; > + FormDelDestMsg(vdest[i], &dmsg); > + msg.type = MsgType::kDelDest; > + memcpy(&msg.info.del, &dmsg, sizeof(dmsg)); > + ret = DispatchTo(msg, dmsg.name); > + // Remove deleted destination from internal database > + allcfgdests_map_.erase(std::remove(allcfgdests_map_.begin(), > + allcfgdests_map_.end(), vdest[i]), > + allcfgdests_map_.end()); > + } > + TRACE_LEAVE(); > + return ret; > +} > + > +ErrCode DestinationHandler::DelAllCfgDest() { > + TRACE_ENTER(); > + ErrCode ret = ErrCode::kOk; > + // Don't care destination name > + const std::string name{""}; > + HandleMsg msg{}; > + msg.type = MsgType::kNoDest; > + ret = DispatchTo(msg, name); > + allcfgdests_map_.clear(); > + TRACE_LEAVE(); > + return ret; > +} > + > +ErrCode DestinationHandler::ProcessCfgChange( > + const VectorString& vdest, ModifyType type) { > + ErrCode ret = ErrCode::kOk; > + TRACE_ENTER(); > + > + // Case #1: perform all destination delete, but no existing at all. > + if ((vdest.size() == 0) && (allcfgdests_map_.size() == 0)) { > + return ErrCode::kOk; > + } > + > + // Case #2: Delete all existing destinations > + if ((vdest.size() == 0) && (allcfgdests_map_.size() != 0)) { > + return DelAllCfgDest(); > + } > + > + // Case #3: Delete destination configurations while no dest cfg yet > + if ((type == ModifyType::kDelete) && > + (vdest.size() != 0) && > + (allcfgdests_map_.size() == 0)) { > + return ErrCode::kDrop; > + } > + > + // Case #4: Delete all destination configurations > + if ((type == ModifyType::kDelete) && > + (vdest.size() == 0) && > + (allcfgdests_map_.size() != 0)) { > + return DelAllCfgDest(); > + } > + > + // Case #5: Delete all destinations but no one existing. > + if ((type == ModifyType::kDelete) && > + (vdest.size() == 0) && > + (allcfgdests_map_.size() == 0)) { > + return ErrCode::kDrop; > + } > + > + switch (type) { > + case ModifyType::kAdd: > + // Add new destinations to existing destinations. > + ret = AddDestConfig(vdest); > + break; > + > + case ModifyType::kReplace: > + // Step #1: Delete all existing destinations if any. > + if (nametype_map_.size() != 0) { > + DelAllCfgDest(); > + } > + // Step #2: Create new destinations > + ret = AddDestConfig(vdest); > + break; > + > + case ModifyType::kDelete: > + ret = DelDestConfig(vdest); > + break; > + > + default: > + LOG_ER("Unknown modify type (%d)", static_cast<int>(type)); > + ret = ErrCode::kErr; > + break; > + } > + > + TRACE_LEAVE2("ret = %d", static_cast<int>(ret)); > + return ret; > +} > + > +ErrCode DestinationHandler::ProcessWriteReq( > + const RecordInfo& info, > + const std::string& name) { > + HandleMsg msg{}; > + RecordMsg record; > + > + msg.type = MsgType::kSendRecord; > + strncpy(record.name, name.c_str(), kMaxChar); > + memcpy(&record.rec, &info, sizeof(RecordInfo)); > + memcpy(&msg.info.rec, &record, sizeof(RecordMsg)); > + return DispatchTo(msg, name); > +} > + > +VectorString DestinationHandler::GetDestinationStatus() { > + // For now, only support destination type "unix" > + // If more than one destination type is support, > + // should go though each, and append the result to output vector. > + return LocalSocketType::Instance().GetAllDestStatus(); > +} > + > +//======================================================== > ====================== > +// Exposed interfaces to outer world > +//======================================================== > ====================== > +bool CfgDestination(const VectorString& vdest, ModifyType type) { > + return (DestinationHandler::Instance().ProcessCfgChange(vdest, type) == > + ErrCode::kOk); > +} > + > +bool WriteToDestination(const RecordData& data, > + const VectorString& destnames) { > + osafassert(data.name != nullptr && data.logrec != nullptr); > + osafassert(data.hostname != nullptr && data.networkname != nullptr); > + > + if (destnames.size() == 0) { > + TRACE("%s No destination goes with this stream (%s)", __func__, > data.name); > + return true; > + } > + > + bool ret = true; > + DestinationHandler::RecordInfo info; > + std::string networkname = (std::string{data.networkname}.length() > 0) ? > + ("." + std::string {data.networkname}) : ""; > + > + // Origin is FQDN = <hostname>[.<networkname>] > + // hostname = where the log record comes from (not active node) > + const std::string origin = std::string {data.hostname} + networkname; > + const std::string msgid = DestinationHandler::Instance().GenerateMsgId( > + data.name, data.isRtStream); > + > + info.msgid = msgid.c_str(); > + info.log_record = data.logrec; > + info.record_id = data.recordId; > + info.stream_dn = data.name; > + info.app_name = data.appname; > + info.severity = data.sev; > + info.time = data.time; > + info.origin = origin.c_str(); > + > + // Go thought all destination names and send to the destination > + // that go with that destination name > + for (const auto& destname : destnames) { > + ret = (DestinationHandler::Instance().ProcessWriteReq(info, destname) > + == ErrCode::kOk); > + } > + > + return ret; > +} > + > +VectorString GetDestinationStatus() { > + return DestinationHandler::Instance().GetDestinationStatus(); > +} > diff --git a/src/log/logd/lgs_dest.h b/src/log/logd/lgs_dest.h > new file mode 100644 > --- /dev/null > +++ b/src/log/logd/lgs_dest.h > @@ -0,0 +1,576 @@ > +/* -*- OpenSAF -*- > + * > + * Copyright Ericsson AB 2017 - All Rights Reserved. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are > licensed > + * under the GNU Lesser General Public License Version 2.1, February 1999. > + * The complete license can be accessed from the following location: > + * http://opensource.org/licenses/lgpl-license.php > + * See the Copying file included with the OpenSAF distribution for full > + * licensing terms. > + * > + * Author(s): Ericsson AB > + * > + */ > + > +#ifndef SRC_LOG_LOGD_LGS_DEST_H_ > +#define SRC_LOG_LOGD_LGS_DEST_H_ > + > +#include <sys/time.h> > +#include <limits.h> > +#include <string> > +#include <map> > +#include <vector> > +#include <utility> > +#include <atomic> > + > +#include "base/log_message.h" > +#include "base/macros.h" > +#include "base/ncsgl_defs.h" > +#include "base/osaf_utility.h" > +#include "base/time.h" > +#include "base/unix_client_socket.h" > + > +// Status of connection to destination > +enum DestinationStatus { > + // Destination is configured and is being connected to > + kActive = 0, > + // Destination is configured but not able to connected to > + kFailed = 1, > + // Configuration only have "name" and "type", but not have > + // configuration value (NILDEST). > + kInactive = 255 > +}; > + > +// This error code is introduced in alternative destination ticket #2258. > +// and for now, only be used internally in lgs_dest files. > +enum ErrCode { > + // No error happens > + kOk = 0, > + // Error happens in general > + kErr, > + // Passing invalid parameters > + kInvalid, > + // Not able to serve the request as the requester is being busy > + kBusy, > + // The request is timeout > + kTimeOut, > + // No memory > + kNoMem, > + // The request/msg is drop as the precondition is not met > + kDrop > +}; > + > +// Type of modification on multiple attribute values. > +// Introduce this own enum as (xvunguy) don't want to > +// have dependent to IMM header file just because we need IMM > modification type. [Lennart] I miss exact definition of what each modify type is doing. Is it the same as IMM multi value modification type? i.e. Add: Add more values. Existing values are not affected Delete: Value(s) that exactly match an existing value is deleted. Other values are not affected Replace: All existing values are replaced by given values > +enum ModifyType { kAdd = 1, kDelete = 2, kReplace = 3 }; > + > +//> > +// Define the "token" possition in destination configuration. > +// Each destination configiration has format "name;type;value". > +// After parsing, the output will put to the vector of strings > +// with format {"name", "type", "value"}. > +// kName : use this index to get "name" > +// kType : use this index to get "kType" > +// kValue: use this index to get "kValue" > +// kSize : this is the maximum size of the dest configuration vector. > +//< > +enum { kName = 0, kType, kValue, kSize }; > + > +//======================================================== > ====================== > +// Two interfaces are published to outside world > +//======================================================== > ====================== > + > +// Typedef for shorten declaration > +// This type could be used to hold set of strings with formats > +// 1) {"name;type;value", etc.} > +// 2) {"name", "type", "value"} > +// 3) etc. > +using VectorString = std::vector<std::string>; > + > +//> > +// @CfgDestination - Configure Destinations. > +// @input vdest: a vector of destination configurations. > +// @input type : modification type on destination configuration attribute. > +// It has equivalent value to @SaImmAttrModificationTypeT. > +// @return: true if everything is OK, otherwise false. > +// > +// Call this function in CCB modified apply callback context, > +// when changes on configuration destination has been verified. > +// @type could be ModifyType::kAdd, ModifyType::kReplace, or > ModifyType::kDelete > +// > +// E.g: > +// 1) Case #1: create 02 new destinations (@type = ModifyType::kAdd) > +// Then, @vdest should contain data {"a;b;c", "d;e;f"} > +// 2) Case #2: modify one destinations. E.g: dest name @d -> @d1 > +// Then, @type = ModifyType::kReplace and @vdest contains data > {"d1;e;f"} > +// 3) Case #3: delete one destination. Eg: dest name @a > +// Then, @type = ModifyType::kDelete and @vdest should contains > {"a;b;c"} > +// 4) Case #4: delete all destinations > +// Then, @type = ModifyType::kDelete and @vdest contains empty vector > {} > +// > +// The function will parse the provided data @vdest, getting > +// destination @name, destination @type, @destination value if any. > +// Then form the right handle msg, and forward it to right destination. > +// > +// This function is blocking. The caller will be blocked until > +// the request is done. > +//< > +bool CfgDestination(const VectorString& vdest, ModifyType type); > + > +//> > +// Carry necessary information to form RFC5424 syslog message. > +// Aim to reduce number of parameter passing to @WriteToDestination > +// @name : log stream distinguish name > +// @logrec : log record that written to local log file > +// @hostname : log record originator > +// @networkname: network name > +// @recordId : record id of @logrec > +// @sev : severity of log record @logrec > +// @time : Same timestamp as in the log record @logrec > +//< > +struct RecordData { > + const char* name; > + const char* logrec; > + const char* hostname; > + const char* networkname; > + const char* appname; > + bool isRtStream; > + uint32_t recordId; > + uint16_t sev; > + timespec time; > + > + RecordData() : name{nullptr}, logrec{nullptr} > + , hostname{nullptr} > + , networkname{nullptr} > + , appname{nullptr} > + , isRtStream{false} > + , recordId{0} > + , sev{0} { > + time = base::ReadRealtimeClock(); > + } > +}; > + > +//> > +// @WriteToDestination - Write @data To Destinations > +// @input: > +// @data : Carry necessary infos including log record. > +// @destnames: Destination names wants to send log record to. > +// @return: true if successfully write to destinations. Oherwise, false. > +// > +// This function is blocking. The caller will be blocked until > +// the request is done. > +//< > +bool WriteToDestination(const RecordData& data, > + const VectorString& destnames); > + > +//> > +// @GetDestinationStatus - Get all destination status > +// @input : none > +// @output: vector of destination status with format > +// {"name,status", "name1,status", etc.} > +//< > +VectorString GetDestinationStatus(); > + > +//> > +// @DestinationHandler class: Dispatch msg to right destination > +// All msgs, if want to come to destination handlers, > +// must go though the DestinationHandler via > +// the interface DestinationHandler::DispachTo() > +// > +// Three kind of DestinationHandler messages supported. > +// 1) Msg contains destination configurations such as > +// destination name, destination type and info (local socket path). > +// > +// 2) Msg contains log record which is going to write to destination. > +// Beside log record, msg may contain other info such as > +// the stream name(DN) it writting to, log record severity, > +// log record originator, log record id, or timestamp. > +// > +// 3) Msg contains destinations that is going to be deleted. > +// This message may have list of deleted destination names. > +// > +// The proper order of DestinationHandler messages should be: > +// (1) -> (2) -> (3) or (1) -> (3). > +// > +// So, if msg contains log record comes(2) to the DestinationHandler first > +// somehow (this probably due to an coding hole, that does not > +// have a good check before sending msg to Dispacher) before > +// the configurating destination msg(1), the DestinationHandler will drop > +// the message, and log info to syslog. > +// The same to the case if deleting destination msg(3) comes first > +// before configuring destination(1), that msg(3) will be drop > +// and log info to syslog. > +// > +// According to information in that messages, the DestinationHandler > +// will do its jobs: interpret the msg, take the right destination > +// and transfer that the msg to the destination type for futher processing. > +//< > +class DestinationHandler { > + public: > + enum { kMaxChar = 255 }; > + enum class DestType { kUnix = 0, kUnknown }; > + //> > + // Handle message types > + // kSendRecord: for writing log record to destination(s). > + // kCfgDest : for configuring destination(s). > + // kDelDest : for deleting destination(s). > + // kNoDest : for deleting all destination(s). > + //< > + enum class MsgType { kSendRecord = 0, kCfgDest, kDelDest, kNoDest }; > + > + //> > + // RecordMsg hold necessary information to forming RFC5424 protocol. > + // @severity : log severity of sent log record. > + // @record_id : record id attached to sent log record. > + // @hash : hash number generated from log stream name (DN) > + // @time : time attached to log record. Using realtime if no set. > + // @origin : node originator of the log record (host & network name). > + // @log_record: the log record that writting to local file. > + // @stream_dn : log stream name(DN). > + //< > + struct RecordInfo { > + const char* stream_dn; > + const char* msgid; > + const char* origin; > + const char* log_record; > + const char* app_name; > + uint16_t severity; > + uint32_t record_id; > + struct timespec time; > + > + // Set default value > + RecordInfo() : stream_dn{nullptr} > + , msgid{nullptr} > + , origin{nullptr} > + , log_record{nullptr} > + , app_name{nullptr} > + , severity{0} > + , record_id{0} { > + time = base::ReadRealtimeClock(); > + } > + }; > + > + //> > + // @RecordMsg - send record @rec to destination name @name > + // @name: destination name > + // @rec : record message > + //> > + struct RecordMsg { > + char name[kMaxChar]; > + RecordInfo rec; > + RecordMsg() : name{0}, rec{} {} > + }; > + > + //> > + // CfgDestMsg - holds information for one destination. > + // @name : name of destination. > + // @type : type of destination. > + // @value: destination configuration > + //< > + struct CfgDestMsg { > + char name[kMaxChar]; > + char type[kMaxChar]; > + char value[kMaxChar]; > + // Clear the values of all types > + CfgDestMsg() : name{0}, type{0}, value{0} {} > + }; > + > + //> > + // DelDestMsg - hold information for one destintaion name > + // @name: refer to the destination name that is going to delete. > + //< > + struct DelDestMsg { > + char name[kMaxChar]; > + DelDestMsg() : name{0} {} > + }; > + > + //> > + // @HandleMsg - Dispatch message format > + // @type: the value depends on @info > + // 1) kSendRecord if @info is RecordMsg > + // 2) kCfgDest if @info is CfgDestMsg > + // 3) kDelDest if @info is DelDestMsg > + // 4) kNoDest if not above > + //< > + struct HandleMsg { > + MsgType type; > + union { > + RecordMsg rec; > + CfgDestMsg cfg; > + DelDestMsg del; > + } info; > + }; > + > + // Unique object instance of @DestinationHandler class > + static DestinationHandler& Instance() { return me_; } > + // Extract the stream name from stream DN > + static std::string GenerateMsgId(const std::string&, bool); > + > + // Do destination configuration basing on input @vdest and @type. > + ErrCode ProcessCfgChange(const VectorString& vdest, ModifyType type); > + // Do write log record @info to destinations @name > + ErrCode ProcessWriteReq(const RecordInfo& info, const std::string& > name); > + // Get all destination status with format > + // {"name,status", "name1,status1", etc.} > + VectorString GetDestinationStatus(); > + > + private: > + // typedef, aim for shorten declaration > + using NameTypeMap = std::map<std::string, DestType>; > + DestinationHandler() : nametype_map_{} {} > + ~DestinationHandler() {} > + > + // Dispatch the @HandleMsg msg to destination @name. > + // Based on the destination @name, the method will find the right > + // destination @type, then forward the message to for futher processing. > + ErrCode DispatchTo(const HandleMsg& msg, const std::string& name); > + // Pack @CfgDestMsg from the string @dest which have pure format > + // "name;type;value". > + void FormCfgDestMsg(const std::string& dest, CfgDestMsg* msg); > + // Pack @DelDestMsg basing on pure destination format > + // "name;type;value". > + void FormDelDestMsg(const std::string& dest, DelDestMsg* msg); > + // Form @CfgDestMsg and dispatch the msg. > + ErrCode AddDestConfig(const VectorString& vdest); > + // Form @DelDestMsg and dispatch the msg. > + ErrCode DelDestConfig(const VectorString& vdest); > + // Form @kNoDest dispatch msg > + ErrCode DelAllCfgDest(); > + // Find name @name from @vec > + bool VectorFind(const VectorString& vec, const std::string name); > + // Return @DestType based on the destination string @type > + DestType GetDestType(const std::string& type); > + DestType ToDestType(const std::string& name); > + // Update the @nametype_map_ whenever getting > + // destination configuration change. > + void UpdateDestTypeDb(const DestinationHandler::HandleMsg&); > + // Update the `logRecordDestinationStatus` > + void UpdateRtDestStatus(); > + > + // The map between destination @name and destination @type > + NameTypeMap nametype_map_; > + // Hold all existing destinations with pure format {"a;b;c", etc} > + VectorString allcfgdests_map_; > + static DestinationHandler me_; > +}; > + > +//> > +// Represent connection to local Unix Domain socket > +// @LocalSocketHandler = Local Socket Destination class > +// > +// Each destination name will have its own instance > +// of this class. It represents the connection > +// to the destination. So, it contains connection status, > +// and the sequence message ID @msg_id_, showing how many logrecord > +// has been written to the socket. This sequence number > +// helps to detect if there is any msg lost so far. > +// > +// When getting a send log record request, this class > +// will form message complying with RFC5424 protocol, putting > +// the log record which has been writen to local file at MSG field, > +// also do fill information to other fields in protocol header. > +// > +// The instance of this class is only deleted if destination info (@value) > +// is deleted from Destination Configuration attribute. > +// > +// It would be wrong if more than one instance of this class > +// pointed to same Domain Unix Socket (same local path name). > +// > +// Regarding connection status, the status will be flushed to > +// latest one at points: > +// 1) Creating new destination: > +// The instance will open a connection to destination, > +// then update its status based on the file descriptor. > +// > +// 2) Change destination to new one: > +// The previous connection is closed and opened a new one > +// toward the destination, then update the status > +// based on the file descriptor. > +// > +// 3) When writing log record to destination: > +// This class is not aware of the destination whether > +// the destination is dead or restarted. > +// If it gets failed to write to socket, the status will be updated. > +// just do that to detect the receiver on destination is dead/restarted. > +// > +// 4) Delete the destination > +// When deleing destination, the instance will be deleted. > +// Then, the status will be updated accordingly. > +// > +// All methods provided by this class should grant access > +// to @LocalSocketType only. No one else can directly > +// invoke any method of this class. > +// > +// With @Send(), if the msg get failed to send to destination, > +// will log info to syslog and return the error code to upper layer. > +// > +// The other information on this class is that, within @Send() > +// method, creating a very big stack buffer - over 65*1024 bytes > +// even the sent RFC msg is quite short (e.g: rfc header + 150 bytes). > +//< > +class LocalSocketHandler { > + public: > + // Set stack size of buffer to maximum size. > + static const uint32_t kBufMaxSize = 65*1024 + 1024; > + // typedef, aim to shorten declaration. > + using RfcBuffer = base::Buffer<kBufMaxSize>; > + > + // This class is only dedicated to LocalSocketType. > + friend class LocalSocketType; > + // Open and connect to the socket > + void Open(); > + // @Close Do nothing > + void Close(); > + // Form RFC5424 and send to the socket > + ErrCode Send(const DestinationHandler::RecordInfo&); > + // Get the socket status > + DestinationStatus GetSockStatus(); > + // Form rfc5424 syslog format > + static void FormRfc5424( > + const DestinationHandler::RecordInfo& msg, > + RfcBuffer* buf); > + > + private: > + explicit LocalSocketHandler(const char*); > + ~LocalSocketHandler(); > + > + // Get the @status_ up-to-date > + void FlushStatus(); > + // Convert AIS log stream severity to syslog severity > + static base::LogMessage::Severity Sev(uint16_t); > + > + // Hold destination info (@value) > + std::string sock_path_; > + base::UnixClientSocket sock_; > + // Hold the connection status > + DestinationStatus status_; > + > + DELETE_COPY_AND_MOVE_OPERATORS(LocalSocketHandler); > +}; > + > +//> > +// @LocalSocketType: Local Socket Destination Type > +// > +// This class represents local socket destination type. > +// Every destinations name with local socket destination type > +// must refer to one and only one instance of this class. > +// Therefore, it has been designed as a singleton pattern. > +// > +// Outside world are not allowed to see any methods of this class > +// except DestinationHandler. Right after analyzing DestinationHandler > messages, > +// knowing what is the destination type, DestinationHandler will pass > +// the message to this class via the unique instance. > +// > +// This class holds a map b/w destination name and @LocalSocketHandler > instance > +// that represents the connection to destination for that destination name. > +// > +// Based on what type of messages come to, it will take specific actions > +// accordingly. > +// 1) Create/Update destination message > +// The message will contain destination name, destination type, > +// and probably destination info (local socket path). > +// Basing on the destination name, the Handler will sure that > +// a) Any such destination name has been created before? > +// b) And any @LocalSocketHandler instance is mapped to that > +// destination name? > +// If no destination name exist in the @map, then Hander will > +// create a new pair <name, @LocalSocketHandler> and update its > destination. > +// If the destination name already exist, will check if configuration info > +// provided in the message is different with the existing or not. > +// We expect it should be different with the existing, otherwise > +// we have code fault somewhere. If different from the existing, closing > +// the old connection, and creating new one and re-map to that name. > +// > +// 2) Sending log record message > +// The message will contain destination name, log record, log stream DN > +// and other infos. Basing on the destination name, the Handler will sure: > +// a) Any such destination name has been created before? > +// b) And any @LocalSocketHandler instance is mapped to that > +// destination name? > +// With this message, the Handler expects the destination name and > +// @LocalSocketHandler must exist. Otherwise, there is a code > +// fault somewhere. > +// and the Handler will drop that message and log info to syslog. > +// If as expected, the Handler will send the message to > +// @LocalSocketHandler instance, in turn, > +// it will form that message according to RF5424 format and > +// send that message to local unix socket. > +// > +// 3) Delete destination message > +// This message will contain only destination name. Basing on that info, > +// the Hanlder will find that name in the @map to see any name is there. > +// If have, do: > +// 1) Close the connection > +// 2) Free resources connected to that destination name > +// Once destination name is deleted, any log record sent to destination > name > +// will be drop or no writing to the destination happens. > +//< > +class LocalSocketType { > + public: > + // @LocalSocketType class is dedicated to @DestinationHandler class. > + // Outside world is not granted to access to this class resources > + friend class DestinationHandler; > + > + // Get @type that represents for @LocalSocketType. This type must be > unique. > + static const std::string Type() { return std::string{name_}; } > + > + private: > + // typedef for the map. Aim to shorten the type declaration. > + using NameTypeMap = std::map<std::string, LocalSocketHandler*>; > + > + LocalSocketType(); > + ~LocalSocketType(); > + > + // Get @LocalSocketHandler instance which has been assigned > + // to this destination @name > + LocalSocketHandler* GetDestHandle(const std::string& name); > + // Get configuration value that goes with this destination name @name > + const char* GetCfgValue(const std::string& name); > + // Find if any destination @name configured so far > + bool FindName(const std::string&) const; > + // Interpret the DestinationHandler msg and forward to right handle > + ErrCode ProcessMsg(const DestinationHandler::HandleMsg&); > + // Processing @Dispacher::RecordMsg msg, then forward to its instance > + // @LocalSocketHandler to form RFC5424 syslog format before writing to > socket. > + ErrCode HandleRecordMsg(const DestinationHandler::RecordMsg&); > + // Processing @DestinationHandler::CfgDestMsg msg. Go thought all > destinations > + // and create or update destination configuration depending on contents. > + ErrCode HandleCfgMsg(const DestinationHandler::CfgDestMsg&); > + // Processing @DestinationHandler::CfgDestMsg msg. Go thought all > deleted > + // destinations names, then do close connections and free resources. > + ErrCode HandleDelCfgMsg(const DestinationHandler::DelDestMsg&); > + // Get destination status for destination @name. > + // The value could be: > + // 1) "active": destination @name is configured and connected to other > end. > + // 2) "failed": destination @name is configured, but not able co connect > to. > + // 3) "inactive": no destination configuration value that goes with @name > + const std::string GetDestinationStatus(const std::string& name); > + // Convert enum status to string > + const std::string DestStatusToStr(DestinationStatus status); > + // Get the unique instance represents this unique type > + // This method should only be used by @DestinationHandler class > + // Get all destination status for Local Socket Destination Typen > + // @return a vector of status, with format > + // {"name,active", "name2,inactive, etc."} > + static const VectorString GetAllDestStatus(); > + // Unique object instance for this @LocalSocketType class > + static LocalSocketType& Instance() { return me_; } > + // Unique instance for this class > + static LocalSocketType me_; > + // The type name of this @LocalSocketType (my identity). > + // Must use this "type" name in destination configuration > + // if want to destinate to local unix socket. > + static const char name_[]; > + > + // Map b/w dest name and its own @LocalSocketHandler instance. > + // Same @name must have same @LocalSocketHandler instance. > + NameTypeMap nametype_map_; > + > + DELETE_COPY_AND_MOVE_OPERATORS(LocalSocketType); > +}; > + > +#endif // SRC_LOG_LOGD_LGS_DEST_H_ > diff --git a/src/log/logd/lgs_evt.cc b/src/log/logd/lgs_evt.cc > --- a/src/log/logd/lgs_evt.cc > +++ b/src/log/logd/lgs_evt.cc > @@ -1,6 +1,7 @@ > /* -*- OpenSAF -*- > * > * (C) Copyright 2008 The OpenSAF Foundation > + * Copyright Ericsson AB 2014, 2017 - All Rights Reserved. [Lennart] Incorrect years in copyright. Should be 2008, 2017 > * > * This program is distributed in the hope that it will be useful, but > * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > @@ -27,6 +28,7 @@ > #include "lgs_imm_gcfg.h" > #include "base/osaf_extended_name.h" > #include "lgs_clm.h" > +#include "lgs_dest.h" > > /* Macro to validate the version */ > #define m_LOG_VER_IS_VALID(ver) \ > @@ -1235,6 +1237,8 @@ static uint32_t proc_write_log_async_msg > void *ckpt_ptr; > uint32_t max_logrecsize = 0; > char node_name[_POSIX_HOST_NAME_MAX]; > + RecordData data; > + timespec time; > > memset(node_name, 0, _POSIX_HOST_NAME_MAX); > strncpy(node_name, evt->node_name, _POSIX_HOST_NAME_MAX); > @@ -1294,6 +1298,35 @@ static uint32_t proc_write_log_async_msg > goto done; > } > > + //> > + // Has successfully written log record to file. > + // Now, send to destination if any destination name set. > + //< > + > + // Streaming not support on alarm/notif streams. > + if ((stream->name == SA_LOG_STREAM_ALARM) || > + (stream->name == SA_LOG_STREAM_NOTIFICATION)) { > + goto checkpoint; > + } > + > + // Packing Record data that carry necessary information > + // to form RFC5424 syslog msg, then send to destination name(s). > + data.name = stream->name.c_str(); > + data.logrec = logOutputString; > + data.hostname = node_name; > + data.networkname = lgs_get_networkname().c_str(); > + data.appname = osaf_extended_name_borrow( > + param->logRecord->logHeader.genericHdr.logSvcUsrName); > + data.isRtStream = stream->isRtStream; > + data.recordId = stream->logRecordId; > + data.sev = param->logRecord->logHeader.genericHdr.logSeverity; > + time.tv_sec = (param->logRecord->logTimeStamp / > (SaTimeT)SA_TIME_ONE_SECOND); > + time.tv_nsec = (param->logRecord->logTimeStamp % > (SaTimeT)SA_TIME_ONE_SECOND); > + data.time = time; > + > + WriteToDestination(data, stream->dest_names); > + > +checkpoint: > /* TODO: send fail back if ack is wanted, Fix counter for application > stream!! > */ > if (cb->ha_state == SA_AMF_HA_ACTIVE) { > if (lgs_is_peer_v2()) { > diff --git a/src/log/logd/lgs_imm.cc b/src/log/logd/lgs_imm.cc > --- a/src/log/logd/lgs_imm.cc > +++ b/src/log/logd/lgs_imm.cc > @@ -45,12 +45,14 @@ > #include "log/logd/lgs_file.h" > #include "log/logd/lgs_recov.h" > #include "log/logd/lgs_config.h" > +#include "log/logd/lgs_dest.h" > #include "base/saf_error.h" > > #include "lgs_mbcsv_v1.h" > #include "lgs_mbcsv_v2.h" > #include "lgs_mbcsv_v3.h" > #include "lgs_mbcsv_v5.h" > +#include "lgs_mbcsv_v7.h" > > /* TYPE DEFINITIONS > * ---------------- > @@ -234,11 +236,33 @@ static uint32_t ckpt_stream_config(log_s > uint32_t rc; > lgsv_ckpt_msg_v1_t ckpt_v1; > lgsv_ckpt_msg_v2_t ckpt_v2; > + lgsv_ckpt_msg_v7_t ckpt_v3; > + > void *ckpt_ptr; > > TRACE_ENTER(); > > - if (lgs_is_peer_v2()) { > + if (lgs_is_peer_v7()) { > + memset(&ckpt_v3, 0, sizeof(ckpt_v3)); > + ckpt_v3.header.ckpt_rec_type = LGS_CKPT_CFG_STREAM; > + ckpt_v3.header.num_ckpt_records = 1; > + ckpt_v3.header.data_len = 1; > + > + ckpt_v3.ckpt_rec.stream_cfg.name = const_cast<char *>(stream- > >name.c_str()); > + ckpt_v3.ckpt_rec.stream_cfg.fileName = const_cast<char *>(stream- > >fileName.c_str()); > + ckpt_v3.ckpt_rec.stream_cfg.pathName = const_cast<char *>(stream- > >pathName.c_str()); > + ckpt_v3.ckpt_rec.stream_cfg.maxLogFileSize = stream->maxLogFileSize; > + ckpt_v3.ckpt_rec.stream_cfg.fixedLogRecordSize = stream- > >fixedLogRecordSize; > + ckpt_v3.ckpt_rec.stream_cfg.logFullAction = stream->logFullAction; > + ckpt_v3.ckpt_rec.stream_cfg.logFullHaltThreshold = stream- > >logFullHaltThreshold; > + ckpt_v3.ckpt_rec.stream_cfg.maxFilesRotated = stream- > >maxFilesRotated; > + ckpt_v3.ckpt_rec.stream_cfg.logFileFormat = stream->logFileFormat; > + ckpt_v3.ckpt_rec.stream_cfg.severityFilter = stream->severityFilter; > + ckpt_v3.ckpt_rec.stream_cfg.logFileCurrent = const_cast<char > *>(stream->logFileCurrent.c_str()); > + ckpt_v3.ckpt_rec.stream_cfg.dest_names = const_cast<char *>(stream- > >stb_dest_names.c_str()); > + ckpt_v3.ckpt_rec.stream_cfg.c_file_close_time_stamp = stream- > >act_last_close_timestamp; > + ckpt_ptr = &ckpt_v3; > + } else if (lgs_is_peer_v2()) { > memset(&ckpt_v2, 0, sizeof(ckpt_v2)); > ckpt_v2.header.ckpt_rec_type = LGS_CKPT_CFG_STREAM; > ckpt_v2.header.num_ckpt_records = 1; > @@ -854,13 +878,14 @@ static SaAisErrorT config_ccb_completed_ > // Note: Multi value attribute > TRACE("logRecordDestinationConfiguration. Values number = %d", > attribute->attrValuesNumber); > - std::vector<std::string> values_vector; > + std::vector<std::string> values_vector{}; > for (uint32_t i=0; i < attribute->attrValuesNumber; i++) { > value = attribute->attrValues[i]; > char *value_str = *(reinterpret_cast<char **>(value)); > values_vector.push_back(value_str); > } > - rc = > lgs_cfg_verify_log_record_destination_configuration(values_vector); > + rc = lgs_cfg_verify_log_record_destination_configuration( > + values_vector, attrMod->modType); > if (rc == -1) { > report_oi_error(immOiHandle, opdata->ccbId, > "%s value is NOT accepted", attribute->attrName); > @@ -1268,6 +1293,20 @@ done: > return rc; > } > > +static bool is_valid_dest_names(const std::vector<std::string>& names) { > + // Delete all destination names [Lennart] A better comment could be: "Stream has no destination name" > + if (names.size() == 0) return true; > + for (const auto& it : names) { [Lennart] It would be more informative if "it" is changed to "name" Example: "for (const auto& name : names)" and "if (name.empty() == true || name.size() == 0)" etc. > + // Empty destination name is invalid > + if (it.empty() == true || it.size() == 0) return false; > + // Contain special characters is not allowed > + if (logutil::isUnixName(it) == false) return false; > + // Name is too long > + if (it.length() > 255) return false; > + } > + return true; > +} > + > /** > * Validate input parameters creation and modify of a persistent stream > * i.e. a stream that has a configuration object. > @@ -1366,12 +1405,16 @@ static SaAisErrorT check_attr_validity(S > if (attribute->attrValuesNumber > 0) { > value = attribute->attrValues[0]; > } else if (opdata->operationType == CCBUTIL_MODIFY) { > - /* An attribute without a value is never valid if modify */ > - report_oi_error(immOiHandle, opdata->ccbId, > - "Attribute %s has no value",attribute->attrName); > - TRACE("Modify: Attribute %s has no value",attribute->attrName); > - rc = SA_AIS_ERR_BAD_OPERATION; > - goto done; > + if (!strcmp(attribute->attrName, "saLogRecordDestination")) { > + // do nothing > + } else { > + /* An attribute without a value is never valid if modify */ > + report_oi_error(immOiHandle, opdata->ccbId, > + "Attribute %s has no value",attribute->attrName); > + TRACE("Modify: Attribute %s has no value",attribute->attrName); > + rc = SA_AIS_ERR_BAD_OPERATION; > + goto done; > + } > } else { > /* If create all attributes will be present also the ones without > * any value. > @@ -1426,8 +1469,24 @@ static SaAisErrorT check_attr_validity(S > i_severityFilter_mod = true; > TRACE("Saved attribute \"%s\" = %d", attribute->attrName, > i_severityFilter); > + } else if (!strcmp(attribute->attrName,"saLogRecordDestination")) { > + std::vector<std::string> vstring{}; > + for (unsigned i = 0; i < attribute->attrValuesNumber; i++) { > + value = attribute->attrValues[i]; > + char *value_str = *(reinterpret_cast<char **>(value)); > + vstring.push_back(value_str); > + } > + if (is_valid_dest_names(vstring) == false) { > + /* Report failed if has special character in file name */ > + rc = SA_AIS_ERR_BAD_OPERATION; > + report_oi_error(immOiHandle, opdata->ccbId, > + "Invalid saLogRecordDestination value"); > + TRACE("Invalid saLogRecordDestination value"); > + goto done; > + } > } > > + > /* Get next attribute or detect no more attributes */ > next: > if (opdata->operationType == CCBUTIL_CREATE) { > @@ -1974,6 +2033,41 @@ static void apply_conf_logDataGroupname( > logDataGroupname_fileown(value_ptr); > } > > +static void apply_config_destinations_change( > + const std::vector<std::string>& vdestcfg, > + SaImmAttrModificationTypeT type, > + lgs_config_chg_t* config_data) { > + switch (type) { > + case SA_IMM_ATTR_VALUES_ADD: > + // Configure destinations > + CfgDestination(vdestcfg, ModifyType::kAdd); > + > lgs_cfgupd_multival_add(LOG_RECORD_DESTINATION_CONFIGURATION, > + vdestcfg, > + config_data); > + break; > + > + case SA_IMM_ATTR_VALUES_DELETE: > + // Configure destinations > + CfgDestination(vdestcfg, ModifyType::kDelete); > + > lgs_cfgupd_multival_delete(LOG_RECORD_DESTINATION_CONFIGURATION, > + vdestcfg, > + config_data); > + break; > + > + case SA_IMM_ATTR_VALUES_REPLACE: > + CfgDestination(vdestcfg, ModifyType::kReplace); > + > lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_CONFIGURATION > , > + vdestcfg, > + config_data); > + break; > + > + default: > + // Shall never happen > + LOG_ER("%s: Unknown modType %d", __FUNCTION__, type); > + osafassert(0); > + }; > +} > + > /** > * Apply changes. Validation is not needed here since all validation is done > in > * the complete callback > @@ -2088,28 +2182,9 @@ static void config_ccb_apply_modify(cons > char *value_str = *(reinterpret_cast<char **>(value)); > values_vector.push_back(value_str); > } > - > - switch (attrMod->modType) { > - case SA_IMM_ATTR_VALUES_ADD: > - > lgs_cfgupd_multival_add(LOG_RECORD_DESTINATION_CONFIGURATION, > - values_vector, > - &config_data); > - break; > - case SA_IMM_ATTR_VALUES_DELETE: > - > lgs_cfgupd_multival_delete(LOG_RECORD_DESTINATION_CONFIGURATION, > - values_vector, > - &config_data); > - break; > - case SA_IMM_ATTR_VALUES_REPLACE: > - > lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_CONFIGURATION > , > - values_vector, > - &config_data); > - break; > - default: > - // Shall never happen > - LOG_ER("%s: Unknown modType %d", __FUNCTION__, attrMod- > >modType); > - osafassert(0); > - }; > + apply_config_destinations_change(values_vector, > + attrMod->modType, > + &config_data); > } > > attrMod = opdata->param.modify.attrMods[i++]; > @@ -2250,6 +2325,15 @@ static SaAisErrorT stream_create_and_con > } else if (!strcmp(ccb->param.create.attrValues[i]->attrName, > "saLogStreamSeverityFilter")) { > (*stream)->severityFilter = *((SaUint32T *) value); > TRACE("severityFilter: %u", (*stream)->severityFilter); > + } else if (!strcmp(ccb->param.create.attrValues[i]->attrName, > + "saLogRecordDestination")) { > + std::vector<std::string> vstring{}; > + for (unsigned ii = 0; ii < ccb->param.create.attrValues[i]- > >attrValuesNumber; ii++) { > + value = ccb->param.create.attrValues[i]->attrValues[ii]; > + char *value_str = *(reinterpret_cast<char **>(value)); > + vstring.push_back(value_str); > + } > + log_stream_add_dest_name(*stream, vstring); > } > } > i++; > @@ -2304,6 +2388,30 @@ static void stream_ccb_apply_create(cons > TRACE_LEAVE(); > } > > +static void apply_destination_names_change( > + log_stream_t* stream, > + const std::vector<std::string>& destname, > + SaImmAttrModificationTypeT type) { > + switch (type) { > + case SA_IMM_ATTR_VALUES_ADD: > + log_stream_add_dest_name(stream, destname); > + break; > + > + case SA_IMM_ATTR_VALUES_DELETE: > + log_stream_delete_dest_name(stream, destname); > + break; > + > + case SA_IMM_ATTR_VALUES_REPLACE: > + log_stream_replace_dest_name(stream, destname); > + break; > + > + default: > + // Shall never happen > + LOG_ER("%s: Unknown modType %d", __FUNCTION__, type); > + osafassert(0); > + } > +} > + > static void stream_ccb_apply_modify(const CcbUtilOperationData_t > *opdata) { > const SaImmAttrModificationT_2 *attrMod; > int i = 0; > @@ -2323,12 +2431,21 @@ static void stream_ccb_apply_modify(cons > > attrMod = opdata->param.modify.attrMods[i++]; > while (attrMod != NULL) { > - void *value; > + void *value = nullptr; > const SaImmAttrValuesT_2 *attribute = &attrMod->modAttr; > > TRACE("attribute %s", attribute->attrName); > - > - value = attribute->attrValues[0]; > + if (attribute->attrValuesNumber != 0) { > + value = attribute->attrValues[0]; > + } else { > + if (!strcmp(attribute->attrName, "saLogRecordDestination")) { > + const std::vector<std::string> dname{}; > + LOG_NO("%s deleted", __func__); > + log_stream_delete_dest_name(stream, dname); > + attrMod = opdata->param.modify.attrMods[i++]; > + continue; > + } > + } > > if (!strcmp(attribute->attrName, "saLogStreamFileName")) { > fileName = *((char **)value); > @@ -2362,6 +2479,14 @@ static void stream_ccb_apply_modify(cons > } else if (!strcmp(attribute->attrName, "saLogStreamSeverityFilter")) { > SaUint32T severityFilter = *((SaUint32T *)value); > stream->severityFilter = severityFilter; > + } else if (!strcmp(attribute->attrName, "saLogRecordDestination")) { > + std::vector<std::string> vstring; > + for (unsigned i = 0; i < attribute->attrValuesNumber; i++) { > + value = attribute->attrValues[i]; > + char *value_str = *(reinterpret_cast<char **>(value)); > + vstring.push_back(value_str); > + } > + apply_destination_names_change(stream, vstring, attrMod- > >modType); > } else { > LOG_ER("Error: Unknown attribute name"); > osafassert(0); > @@ -2613,6 +2738,7 @@ static SaAisErrorT stream_create_and_con > const_cast<char *>("saLogStreamLogFullHaltThreshold"), > const_cast<char *>("saLogStreamMaxFilesRotated"), > const_cast<char *>("saLogStreamLogFileFormat"), > + const_cast<char *>("saLogRecordDestination"), > const_cast<char *>("saLogStreamSeverityFilter"), > const_cast<char *>("saLogStreamCreationTimestamp"), > NULL > @@ -2696,6 +2822,14 @@ static SaAisErrorT stream_create_and_con > } else if (!strcmp(attribute->attrName, "saLogStreamSeverityFilter")) { > stream->severityFilter = *((SaUint32T *)value); > TRACE("severityFilter: %u", stream->severityFilter); > + } else if (!strcmp(attribute->attrName, "saLogRecordDestination")) { > + std::vector<std::string> vstring{}; > + for (unsigned i = 0; i < attribute->attrValuesNumber; i++) { > + value = attribute->attrValues[i]; > + char *value_str = *(reinterpret_cast<char **>(value)); > + vstring.push_back(value_str); > + } > + log_stream_add_dest_name(stream, vstring); > } else if (!strcmp(attribute->attrName, > "saLogStreamCreationTimestamp")) { > if (attribute->attrValuesNumber != 0) { > /* Restore creation timestamp if exist > diff --git a/src/log/logd/lgs_main.cc b/src/log/logd/lgs_main.cc > --- a/src/log/logd/lgs_main.cc > +++ b/src/log/logd/lgs_main.cc > @@ -39,6 +39,7 @@ > #include "lgs_recov.h" > #include "osaf/immutil/immutil.h" > #include "lgs_clm.h" > +#include "log/logd/lgs_dest.h" > > /* > ========================================================== > ============== > * DEFINITIONS > @@ -371,6 +372,7 @@ done: > uint32_t initialize_for_assignment(lgs_cb_t *cb, SaAmfHAStateT ha_state) { > const char *logsv_root_dir = NULL; > const char *logsv_data_groupname = NULL; > + const VectorString *vdest = nullptr; [Lennart] Declaring *vedest like this somewhat hides the actual type (VectorString is not a well-known type) and since *vdest is used with lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION) and LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION is defined as a std::vector<std::string> it is a bit unclear > TRACE_ENTER2("ha_state = %d", (int) ha_state); > uint32_t rc = NCSCC_RC_SUCCESS; > > @@ -393,6 +395,12 @@ uint32_t initialize_for_assignment(lgs_c > logsv_data_groupname = static_cast<const char > *>(lgs_cfg_get(LGS_IMM_DATA_GROUPNAME)); > LOG_NO("LOG root directory is: \"%s\"", logsv_root_dir); > LOG_NO("LOG data group is: \"%s\"", logsv_data_groupname); > + vdest = reinterpret_cast<const VectorString*>( > + > lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION)); > + osafassert(vdest != nullptr); > + if (vdest->size() > 1) { > + CfgDestination(*vdest, ModifyType::kAdd); > + } > > /* Initialize file handling thread > * Configuration must have been initialized > diff --git a/src/log/logd/lgs_mbcsv.cc b/src/log/logd/lgs_mbcsv.cc > --- a/src/log/logd/lgs_mbcsv.cc > +++ b/src/log/logd/lgs_mbcsv.cc > @@ -1,6 +1,7 @@ > /* -*- OpenSAF -*- > * > * (C) Copyright 2008 The OpenSAF Foundation > + * Copyright Ericsson AB 2014, 2017 - All Rights Reserved. [Lennart] Incorrect copyright year > * > * This program is distributed in the hope that it will be useful, but > * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > @@ -20,11 +21,14 @@ > #include "base/ncssysf_mem.h" > #include "base/osaf_time.h" > > +#include "log/logd/lgs_mbcsv_v7.h" > #include "lgs_mbcsv_v5.h" > #include "lgs_mbcsv_v3.h" > #include "lgs_mbcsv_v2.h" > #include "lgs_mbcsv_v1.h" > #include "lgs_recov.h" > +#include "osaf/immutil/immutil.h" > +#include "log/logd/lgs_dest.h" > > /* > LGS_CKPT_DATA_HEADER > @@ -360,6 +364,18 @@ bool lgs_is_peer_v5() { > } > > /** > + * Check if peer is version 7 (or later) > + * @return bool > + */ > +bool lgs_is_peer_v7() { > + if (lgs_cb->mbcsv_peer_version >= LGS_MBCSV_VERSION_7) { > + return true; > + } else { > + return false; > + } > +} > + > +/** > * Check if configured for split file system. > * If other node is version 1 split file system mode is not applicable. > * > @@ -750,6 +766,7 @@ static uint32_t edu_enc_reg_list(lgs_cb_ > > ********************************************************** > ******************/ > > static uint32_t ckpt_encode_async_update(lgs_cb_t *lgs_cb, EDU_HDL > edu_hdl, NCS_MBCSV_CB_ARG *cbk_arg) { > + lgsv_ckpt_msg_v7_t *data_v7 = NULL; > lgsv_ckpt_msg_v5_t *data_v5 = NULL; > lgsv_ckpt_msg_v3_t *data_v3 = NULL; > lgsv_ckpt_msg_v2_t *data_v2 = NULL; > @@ -761,7 +778,12 @@ static uint32_t ckpt_encode_async_update > > TRACE_ENTER(); > /* Set reo_hdl from callback arg to ckpt_rec */ > - if (lgs_is_peer_v5()) { > + if (lgs_is_peer_v7()) { > + data_v7 = reinterpret_cast<lgsv_ckpt_msg_v7_t *>( > + static_cast<long>(cbk_arg->info.encode.io_reo_hdl)); > + vdata = data_v7; > + edp_function = edp_ed_ckpt_msg_v7; > + } else if (lgs_is_peer_v5()) { > data_v5 = reinterpret_cast<lgsv_ckpt_msg_v5_t *>( > static_cast<long>(cbk_arg->info.encode.io_reo_hdl)); > vdata = data_v5; > @@ -1047,7 +1069,11 @@ static uint32_t ckpt_decode_log_cfg_stre > void *stream_cfg; > EDU_PROG_HANDLER edp_function; > > - if (lgs_is_peer_v2()) { > + if (lgs_is_peer_v7()) { > + lgsv_ckpt_msg_v7_t *ckpt_msg_v7 = static_cast<lgsv_ckpt_msg_v7_t > *>(ckpt_msg); > + stream_cfg = &ckpt_msg_v7->ckpt_rec.stream_cfg; > + edp_function = edp_ed_cfg_stream_rec_v7; > + } else if (lgs_is_peer_v2()) { > lgsv_ckpt_msg_v2_t *ckpt_msg_v2 = static_cast<lgsv_ckpt_msg_v2_t > *>(ckpt_msg); > stream_cfg = &ckpt_msg_v2->ckpt_rec.stream_cfg; > edp_function = edp_ed_cfg_stream_rec_v2; > @@ -1069,11 +1095,16 @@ static uint32_t ckpt_decode_log_cfg(lgs_ > uint32_t rc = NCSCC_RC_SUCCESS; > void *lgs_cfg = NULL; > EDU_PROG_HANDLER edp_function = NULL; > + lgsv_ckpt_msg_v7_t *ckpt_msg_v7; > lgsv_ckpt_msg_v5_t *ckpt_msg_v5; > lgsv_ckpt_msg_v3_t *ckpt_msg_v3; > lgsv_ckpt_msg_v2_t *ckpt_msg_v2; > > - if (lgs_is_peer_v5()) { > + if (lgs_is_peer_v7()) { > + ckpt_msg_v7 = static_cast<lgsv_ckpt_msg_v7_t *>(ckpt_msg); > + lgs_cfg = &ckpt_msg_v7->ckpt_rec.lgs_cfg; > + edp_function = edp_ed_lgs_cfg_rec_v5; > + } else if (lgs_is_peer_v5()) { > ckpt_msg_v5 = static_cast<lgsv_ckpt_msg_v5_t *>(ckpt_msg); > lgs_cfg = &ckpt_msg_v5->ckpt_rec.lgs_cfg; > edp_function = edp_ed_lgs_cfg_rec_v5; > @@ -1111,6 +1142,8 @@ static uint32_t ckpt_decode_async_update > lgsv_ckpt_msg_v3_t *ckpt_msg_v3 = &msg_v3; > lgsv_ckpt_msg_v5_t msg_v5; > lgsv_ckpt_msg_v5_t *ckpt_msg_v5 = &msg_v5; > + lgsv_ckpt_msg_v7_t msg_v7; > + lgsv_ckpt_msg_v7_t *ckpt_msg_v7 = &msg_v7; > void *ckpt_msg; > lgsv_ckpt_header_t hdr, *hdr_ptr = &hdr; > > @@ -1130,7 +1163,10 @@ static uint32_t ckpt_decode_async_update > > TRACE_2("\tckpt_rec_type: %d ", (int)hdr_ptr->ckpt_rec_type); > > - if (lgs_is_peer_v5()) { > + if (lgs_is_peer_v7()) { > + ckpt_msg_v7->header = hdr; > + ckpt_msg = ckpt_msg_v7; > + } else if (lgs_is_peer_v5()) { > ckpt_msg_v5->header = hdr; > ckpt_msg = ckpt_msg_v5; > } else if (lgs_is_peer_v4()) { > @@ -1148,7 +1184,9 @@ static uint32_t ckpt_decode_async_update > switch (hdr_ptr->ckpt_rec_type) { > case LGS_CKPT_CLIENT_INITIALIZE: > TRACE_2("\tINITIALIZE REC: UPDATE"); > - if (lgs_is_peer_v5()) { > + if (lgs_is_peer_v7()) { > + reg_rec = &ckpt_msg_v7->ckpt_rec.initialize_client; > + } else if (lgs_is_peer_v5()) { > reg_rec = &ckpt_msg_v5->ckpt_rec.initialize_client; > } else if (lgs_is_peer_v4()) { > reg_rec = &ckpt_msg_v3->ckpt_rec.initialize_client; > @@ -1173,7 +1211,9 @@ static uint32_t ckpt_decode_async_update > > case LGS_CKPT_OPEN_STREAM: /* 4 */ > TRACE_2("\tSTREAM OPEN: UPDATE"); > - if (lgs_is_peer_v5()) { > + if (lgs_is_peer_v7()) { > + stream_open = &ckpt_msg_v7->ckpt_rec.stream_open; > + } else if (lgs_is_peer_v5()) { > stream_open = &ckpt_msg_v5->ckpt_rec.stream_open; > } else if (lgs_is_peer_v4()) { > stream_open = &ckpt_msg_v3->ckpt_rec.stream_open; > @@ -1417,13 +1457,17 @@ static uint32_t process_ckpt_data(lgs_cb > lgsv_ckpt_msg_v2_t *data_v2; > lgsv_ckpt_msg_v3_t *data_v3; > lgsv_ckpt_msg_v5_t *data_v5; > + lgsv_ckpt_msg_v7_t *data_v7; > > if ((!cb) || (data == NULL)) { > TRACE("%s - FAILED: (!cb) || (data == NULL)", __FUNCTION__); > return (rc = NCSCC_RC_FAILURE); > } > > - if (lgs_is_peer_v5()) { > + if (lgs_is_peer_v7()) { > + data_v7 = static_cast<lgsv_ckpt_msg_v7_t *>(data); > + lgsv_ckpt_msg_type = data_v7->header.ckpt_rec_type; > + } else if (lgs_is_peer_v5()) { > data_v5 = static_cast<lgsv_ckpt_msg_v5_t *>(data); > lgsv_ckpt_msg_type = data_v5->header.ckpt_rec_type; > } else if (lgs_is_peer_v4()) { > @@ -1834,6 +1878,7 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > lgs_ckpt_stream_open_t *param; > log_stream_t *stream; > int pos = 0, err = 0; > + SaNameT objectName; > > TRACE_ENTER(); > > @@ -1902,7 +1947,18 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > stream->logFileCurrent = param->logFileCurrent; > stream->stb_prev_actlogFileCurrent = param->logFileCurrent; > stream->stb_logFileCurrent = param->logFileCurrent; > - stream->isRtStream = SA_TRUE; > + > + osaf_extended_name_lend(param->logStreamName, &objectName); > + SaImmClassNameT className = immutil_get_className(&objectName); > + if (className != nullptr && strcmp(className, "SaLogStreamConfig") == > 0) { > + stream->isRtStream = SA_FALSE; > + } else { > + stream->isRtStream = SA_TRUE; > + } > + if (className != nullptr) free(className); > + // Not support adding destination name into saLogStreamOpen() > parameters. > + stream->dest_names.clear(); > + stream->stb_dest_names = ""; > } > > /* If configured for split file system files shall be opened on stand by > @@ -2061,12 +2117,28 @@ static uint32_t ckpt_proc_cfg_stream(lgs > SaUint32T logFullHaltThreshold; /* !app log stream */ > SaUint32T maxFilesRotated; > char *logFileFormat; > + char *dest_names = nullptr; > SaUint32T severityFilter; > char *logFileCurrent; > > TRACE_ENTER(); > > - if (lgs_is_peer_v2()) { > + if (lgs_is_peer_v7()) { > + lgsv_ckpt_msg_v7_t *data_v7 = static_cast<lgsv_ckpt_msg_v7_t > *>(data); > + name = data_v7->ckpt_rec.stream_cfg.name; > + fileName = data_v7->ckpt_rec.stream_cfg.fileName; > + pathName = data_v7->ckpt_rec.stream_cfg.pathName; > + maxLogFileSize = data_v7->ckpt_rec.stream_cfg.maxLogFileSize; > + fixedLogRecordSize = data_v7->ckpt_rec.stream_cfg.fixedLogRecordSize; > + logFullAction = data_v7->ckpt_rec.stream_cfg.logFullAction; > + logFullHaltThreshold = data_v7- > >ckpt_rec.stream_cfg.logFullHaltThreshold; > + maxFilesRotated = data_v7->ckpt_rec.stream_cfg.maxFilesRotated; > + logFileFormat = data_v7->ckpt_rec.stream_cfg.logFileFormat; > + severityFilter = data_v7->ckpt_rec.stream_cfg.severityFilter; > + logFileCurrent = data_v7->ckpt_rec.stream_cfg.logFileCurrent; > + dest_names = data_v7->ckpt_rec.stream_cfg.dest_names; > + closetime = data_v7->ckpt_rec.stream_cfg.c_file_close_time_stamp; > + } else if (lgs_is_peer_v2()) { > lgsv_ckpt_msg_v2_t *data_v2 = static_cast<lgsv_ckpt_msg_v2_t > *>(data); > name = data_v2->ckpt_rec.stream_cfg.name; > fileName = data_v2->ckpt_rec.stream_cfg.fileName; > @@ -2119,6 +2191,14 @@ static uint32_t ckpt_proc_cfg_stream(lgs > strcpy(stream->logFileFormat, logFileFormat); > stream->severityFilter = severityFilter; > stream->logFileCurrent = logFileCurrent; > + TRACE("dest_names: %s", dest_names); > + if (strlen(dest_names) == 0) { > + stream->stb_dest_names = ""; > + stream->dest_names.clear(); > + } else { > + stream->stb_dest_names = std::string{dest_names}; > + stream->dest_names = logutil::Parser(stream->stb_dest_names, ";"); > + } > > /* If split file mode, update standby files */ > if (lgs_is_split_file_system()) { > @@ -2178,7 +2258,10 @@ uint32_t lgs_ckpt_send_async(lgs_cb_t *c > > TRACE_ENTER(); > > - if (lgs_is_peer_v5()) { > + if (lgs_is_peer_v7()) { > + lgsv_ckpt_msg_v7_t *ckpt_rec_v7 = static_cast<lgsv_ckpt_msg_v7_t > *>(ckpt_rec); > + ckpt_rec_type = ckpt_rec_v7->header.ckpt_rec_type; > + } else if (lgs_is_peer_v5()) { > lgsv_ckpt_msg_v5_t *ckpt_rec_v5 = static_cast<lgsv_ckpt_msg_v5_t > *>(ckpt_rec); > ckpt_rec_type = ckpt_rec_v5->header.ckpt_rec_type; > } else if (lgs_is_peer_v4()) { > diff --git a/src/log/logd/lgs_mbcsv.h b/src/log/logd/lgs_mbcsv.h > --- a/src/log/logd/lgs_mbcsv.h > +++ b/src/log/logd/lgs_mbcsv.h > @@ -2,6 +2,7 @@ > * File: lgs_mbcsv.h > * > * (C) Copyright 2008 The OpenSAF Foundation > + * Copyright Ericsson AB 2014, 2017 - All Rights Reserved. [Lennart] Incorrect copyright year > * > * This program is distributed in the hope that it will be useful, but > * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > @@ -38,9 +39,11 @@ > #define LGS_MBCSV_VERSION_2 2 > #define LGS_MBCSV_VERSION_4 4 > #define LGS_MBCSV_VERSION_5 5 > +#define LGS_MBCSV_VERSION_7 7 > + > > /* Current version */ > -#define LGS_MBCSV_VERSION 5 > +#define LGS_MBCSV_VERSION 7 > #define LGS_MBCSV_VERSION_MIN 1 > > /* Checkpoint message types(Used as 'reotype' w.r.t mbcsv) */ > @@ -106,6 +109,7 @@ bool lgs_is_peer_v2(); > bool lgs_is_peer_v3(); > bool lgs_is_peer_v4(); > bool lgs_is_peer_v5(); > +bool lgs_is_peer_v7(); > bool lgs_is_split_file_system(); > uint32_t lgs_mbcsv_dispatch(NCS_MBCSV_HDL mbcsv_hdl); > void lgs_free_edu_mem(char *ptr); > diff --git a/src/log/logd/lgs_mbcsv_v5.cc b/src/log/logd/lgs_mbcsv_v5.cc > --- a/src/log/logd/lgs_mbcsv_v5.cc > +++ b/src/log/logd/lgs_mbcsv_v5.cc > @@ -1,6 +1,7 @@ > /* -*- OpenSAF -*- > * > * (C) Copyright 2015 The OpenSAF Foundation > + * Copyright Ericsson AB 2015, 2017 - All Rights Reserved. > * File: lgs_mbcsv_v5.c > * > * This program is distributed in the hope that it will be useful, but > @@ -22,6 +23,7 @@ > */ > > #include "lgs_mbcsv_v5.h" > +#include "log/logd/lgs_dest.h" > > > /********************************************************** > ****************** > * Name : ckpt_proc_lgs_cfg > @@ -132,6 +134,14 @@ uint32_t ckpt_proc_lgs_cfg_v5(lgs_cb_t * > TRACE("\tUpdating mailbox limits"); > (void) lgs_configure_mailbox(); > } > + // Only support destination configuration since V7 > + if (lgs_is_peer_v7()) { > + const VectorString *vdest = nullptr; > + vdest = reinterpret_cast<const VectorString*>( > + > lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION)); > + osafassert(vdest != nullptr); > + CfgDestination(*vdest, ModifyType::kReplace); > + } > > /* Free buffer allocated by the EDU encoder */ > if (param->buffer == NULL) { > diff --git a/src/log/logd/lgs_mbcsv_v7.cc b/src/log/logd/lgs_mbcsv_v7.cc > new file mode 100644 > --- /dev/null > +++ b/src/log/logd/lgs_mbcsv_v7.cc > @@ -0,0 +1,177 @@ > +/* -*- OpenSAF -*- > + * > + * (C) Copyright 2017 The OpenSAF Foundation > + * Copyright Ericsson AB 2017 - All Rights Reserved. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are > licensed > + * under the GNU Lesser General Public License Version 2.1, February 1999. > + * The complete license can be accessed from the following location: > + * http://opensource.org/licenses/lgpl-license.php > + * See the Copying file included with the OpenSAF distribution for full > + * licensing terms. > + * > + * Author(s): Ericsson AB > + * > + */ > + > +#include "log/logd/lgs_mbcsv_v7.h" > + > +/********************************************************* > ******************* > + * Name : edp_ed_cfg_stream_rec > + * > + * Description : This function is an EDU program for encoding/decoding > + * lgsv checkpoint cfg_update_stream log rec. > + * > + * Arguments : EDU_HDL - pointer to edu handle, > + * EDU_TKN - internal edu token to help encode/decode, > + * POINTER to the structure to encode/decode from/to, > + * data length specifying number of structures, > + * EDU_BUF_ENV - pointer to buffer for encoding/decoding. > + * op - operation type being encode/decode. > + * EDU_ERR - out param to indicate errors in processing. > + * > + * Return Values : NCSCC_RC_SUCCESS/NCSCC_RC_FAILURE > + * > + * Notes : None. > + > ********************************************************** > *******************/ > + > +uint32_t edp_ed_cfg_stream_rec_v7(EDU_HDL *edu_hdl, EDU_TKN > *edu_tkn, > + NCSCONTEXT ptr, uint32_t *ptr_data_len, > + EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, > EDU_ERR > *o_err) { > + uint32_t rc = NCSCC_RC_SUCCESS; > + lgs_ckpt_stream_cfg_v3_t *ckpt_stream_cfg_msg_ptr = NULL, > **ckpt_stream_cfg_msg_dec_ptr; > + > + EDU_INST_SET ckpt_stream_cfg_rec_ed_rules[] = { > + {EDU_START, edp_ed_cfg_stream_rec_v7, 0, 0, 0, > sizeof(lgs_ckpt_stream_cfg_v3_t), 0, NULL}, > + {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->name, 0, NULL}, > + {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->fileName, 0, NULL}, > + {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->pathName, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->maxLogFileSize, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->fixedLogRecordSize, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->haProperty, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->logFullAction, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->logFullHaltThreshold, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->maxFilesRotated, 0, NULL}, > + {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->logFileFormat, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->severityFilter, 0, NULL}, > + {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->logFileCurrent, 0, NULL}, > + {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->dest_names, 0, NULL}, > + {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t > *)0)->c_file_close_time_stamp, 0, NULL}, > + {EDU_END, 0, 0, 0, 0, 0, 0, NULL}, > + }; > + > + if (op == EDP_OP_TYPE_ENC) { > + ckpt_stream_cfg_msg_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t > *>(ptr); > + } else if (op == EDP_OP_TYPE_DEC) { > + ckpt_stream_cfg_msg_dec_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t > **>(ptr); > + if (*ckpt_stream_cfg_msg_dec_ptr == NULL) { > + *o_err = EDU_ERR_MEM_FAIL; > + return NCSCC_RC_FAILURE; > + } > + memset(*ckpt_stream_cfg_msg_dec_ptr, '\0', > sizeof(lgs_ckpt_stream_cfg_v3_t)); > + ckpt_stream_cfg_msg_ptr = *ckpt_stream_cfg_msg_dec_ptr; > + } else { > + ckpt_stream_cfg_msg_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t > *>(ptr); > + } > + > + rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn, > + ckpt_stream_cfg_rec_ed_rules, > + ckpt_stream_cfg_msg_ptr, > + ptr_data_len, > + buf_env, op, o_err); > + return rc; > +} > + > +/********************************************************* > ******************* > + * Name : edp_ed_ckpt_msg_v7 > + * > + * Description : This function is an EDU program for encoding/decoding > + * lgsv checkpoint messages. This program runs the > + * edp_ed_hdr_rec program first to decide the > + * checkpoint message type based on which it will call the > + * appropriate EDU programs for the different checkpoint > + * messages. > + * > + * Arguments : EDU_HDL - pointer to edu handle, > + * EDU_TKN - internal edu token to help encode/decode, > + * POINTER to the structure to encode/decode from/to, > + * data length specifying number of structures, > + * EDU_BUF_ENV - pointer to buffer for encoding/decoding. > + * op - operation type being encode/decode. > + * EDU_ERR - out param to indicate errors in processing. > + * > + * Return Values : NCSCC_RC_SUCCESS/NCSCC_RC_FAILURE > + * > + * Notes : None. > + > ********************************************************** > *******************/ > + > +uint32_t edp_ed_ckpt_msg_v7(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn, > + NCSCONTEXT ptr, uint32_t *ptr_data_len, > EDU_BUF_ENV > *buf_env, > + EDP_OP_TYPE op, EDU_ERR *o_err) { > + uint32_t rc = NCSCC_RC_SUCCESS; > + lgsv_ckpt_msg_v7_t *ckpt_msg_ptr = NULL, **ckpt_msg_dec_ptr; > + > + EDU_INST_SET ckpt_msg_ed_rules[] = { > + {EDU_START, edp_ed_ckpt_msg_v7, 0, 0, 0, sizeof(lgsv_ckpt_msg_v7_t), > 0, NULL}, > + {EDU_EXEC, edp_ed_header_rec, 0, 0, 0, (long)&((lgsv_ckpt_msg_v7_t > *)0)->header, 0, NULL}, > + > + {EDU_TEST, ncs_edp_uns32, 0, 0, 0, (long)&((lgsv_ckpt_msg_v7_t *)0)- > >header, 0, > + (EDU_EXEC_RTINE)ckpt_msg_test_type}, > + > + /* Reg Record */ > + {EDU_EXEC, edp_ed_reg_rec, 0, 0, static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.initialize_client, 0, NULL}, > + > + /* Finalize record */ > + {EDU_EXEC, edp_ed_finalize_rec_v2, 0, 0, static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.finalize_client, 0, NULL}, > + > + /* write log Record */ > + {EDU_EXEC, edp_ed_write_rec_v2, 0, 0, static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.write_log, 0, NULL}, > + > + /* Open stream */ > + {EDU_EXEC, edp_ed_open_stream_rec, 0, 0, static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_open, 0, NULL}, > + > + /* Close stream */ > + {EDU_EXEC, edp_ed_close_stream_rec_v2, 0, 0, > static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_close, 0, NULL}, > + > + /* Agent dest */ > + {EDU_EXEC, edp_ed_agent_down_rec_v2, 0, 0, > static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_cfg, 0, NULL}, > + > + /* Cfg stream */ > + {EDU_EXEC, edp_ed_cfg_stream_rec_v7, 0, 0, > static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.stream_cfg, 0, NULL}, > + > + /* Lgs cfg */ > + {EDU_EXEC, edp_ed_lgs_cfg_rec_v5, 0, 0, static_cast<int>(EDU_EXIT), > + (long)&((lgsv_ckpt_msg_v7_t *)0)->ckpt_rec.lgs_cfg, 0, NULL}, > + > + {EDU_END, 0, 0, 0, 0, 0, 0, NULL}, > + }; > + > + if (op == EDP_OP_TYPE_ENC) { > + ckpt_msg_ptr = static_cast<lgsv_ckpt_msg_v7_t *>(ptr); > + } else if (op == EDP_OP_TYPE_DEC) { > + ckpt_msg_dec_ptr = static_cast<lgsv_ckpt_msg_v7_t **>(ptr); > + if (*ckpt_msg_dec_ptr == NULL) { > + *o_err = EDU_ERR_MEM_FAIL; > + return NCSCC_RC_FAILURE; > + } > + memset(*ckpt_msg_dec_ptr, '\0', sizeof(lgsv_ckpt_msg_v7_t)); > + ckpt_msg_ptr = *ckpt_msg_dec_ptr; > + } else { > + ckpt_msg_ptr = static_cast<lgsv_ckpt_msg_v7_t *>(ptr); > + } > + > + rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn, ckpt_msg_ed_rules, > + ckpt_msg_ptr, ptr_data_len, buf_env, op, o_err); > + > + return rc; > + > +} > diff --git a/src/log/logd/lgs_mbcsv_v7.h b/src/log/logd/lgs_mbcsv_v7.h > new file mode 100644 > --- /dev/null > +++ b/src/log/logd/lgs_mbcsv_v7.h > @@ -0,0 +1,67 @@ > +/* -*- OpenSAF -*- > + * > + * (C) Copyright 2017 The OpenSAF Foundation > + * Copyright Ericsson AB 2017 - All Rights Reserved. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are > licensed > + * under the GNU Lesser General Public License Version 2.1, February 1999. > + * The complete license can be accessed from the following location: > + * http://opensource.org/licenses/lgpl-license.php > + * See the Copying file included with the OpenSAF distribution for full > + * licensing terms. > + * > + * Author(s): Ericsson AB > + * > + */ > + > +#ifndef SRC_LOG_LOGD_LGS_MBCSV_V7_H_ > +#define SRC_LOG_LOGD_LGS_MBCSV_V7_H_ > + > +#include "log/logd/lgs.h" > +#include "log/logd/lgs_config.h" > +#include "log/logd/lgs_mbcsv.h" > +#include "log/logd/lgs_mbcsv_v2.h" > +#include "log/logd/lgs_mbcsv_v5.h" > + > +/* Structures for Checkpoint data ver 7 (to be replicated at the standby) */ > + > +typedef struct { > + char *name; > + char *fileName; > + char *pathName; > + SaUint64T maxLogFileSize; > + SaUint32T fixedLogRecordSize; > + SaBoolT haProperty; /* app log stream only */ > + SaLogFileFullActionT logFullAction; > + SaUint32T logFullHaltThreshold; /* !app log stream */ > + SaUint32T maxFilesRotated; > + char *logFileFormat; > + SaUint32T severityFilter; > + char *logFileCurrent; > + char *dest_names; > + uint64_t c_file_close_time_stamp; /* Time in sec for file rename on Active > */ > +} lgs_ckpt_stream_cfg_v3_t; > + > +typedef struct { > + lgsv_ckpt_header_t header; > + union { > + lgs_ckpt_initialize_msg_t initialize_client; > + lgs_ckpt_finalize_msg_v2_t finalize_client; > + lgs_ckpt_write_log_v2_t write_log; > + lgs_ckpt_agent_down_v2_t agent_down; > + lgs_ckpt_stream_open_t stream_open; > + lgs_ckpt_stream_close_v2_t stream_close; > + lgs_ckpt_stream_cfg_v3_t stream_cfg; > + lgs_ckpt_lgs_cfg_v5_t lgs_cfg; > + } ckpt_rec; > +} lgsv_ckpt_msg_v7_t; > + > +uint32_t edp_ed_cfg_stream_rec_v7(EDU_HDL *edu_hdl, EDU_TKN > *edu_tkn, > + NCSCONTEXT ptr, uint32_t *ptr_data_len, > + EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, > EDU_ERR > *o_err); > +uint32_t edp_ed_ckpt_msg_v7(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn, > + NCSCONTEXT ptr, uint32_t *ptr_data_len, > EDU_BUF_ENV > *buf_env, EDP_OP_TYPE op, EDU_ERR *o_err); > + > +#endif // SRC_LOG_LOGD_LGS_MBCSV_V7_H_ > diff --git a/src/log/logd/lgs_stream.cc b/src/log/logd/lgs_stream.cc > --- a/src/log/logd/lgs_stream.cc > +++ b/src/log/logd/lgs_stream.cc > @@ -27,6 +27,9 @@ > * Examples can be found in this file, e.g. function fileopen(...) below > */ > > +#include "log/logd/lgs_stream.h" > +#include <algorithm> > + > #include "log/logd/lgs.h" > #include "lgs_config.h" > #include "log/logd/lgs_file.h" > @@ -480,7 +483,6 @@ int lgs_populate_log_stream( > o_stream->logRecordId = logRecordId; > o_stream->stb_logRecordId = 0; > o_stream->isRtStream = SA_TRUE; > - > o_stream->logFileFormat = strdup(logFileFormat); > if (o_stream->logFileFormat == NULL) { > LOG_WA("Failed to allocate memory for logFileFormat"); > @@ -654,6 +656,7 @@ log_stream_t *log_stream_new(const std:: > stream->creationTimeStamp = lgs_get_SaTime(); > stream->severityFilter = 0x7f; /* by default all levels are allowed */ > stream->isRtStream = SA_FALSE; > + stream->dest_names.clear(); > > /* Initiate local or shared stream file descriptor dependant on shared or > * split file system > @@ -1492,3 +1495,58 @@ bool check_max_stream() { > return (numb_of_streams < stream_array_size ? false:true); > } > > +void log_stream_add_dest_name(log_stream_t *stream, > + const std::vector<std::string>& names) { > + osafassert(stream != nullptr); > + for (const auto& it : names) { > + stream->dest_names.push_back(it); > + } > + > + // Prepare stb_dest_names for checkingpoint to standby > + log_stream_form_dest_names(stream); > +} > + > +void log_stream_replace_dest_name(log_stream_t *stream, > + const std::vector<std::string>& names) { > + osafassert(stream != nullptr); > + stream->dest_names = names; > + // Prepare stb_dest_names for checkingpoint to standby > + log_stream_form_dest_names(stream); > +} > + > +void log_stream_delete_dest_name(log_stream_t *stream, > + const std::vector<std::string>& names) { > + osafassert(stream != nullptr); > + if (names.size() != 0) { > + for (const auto& it : names) { > + // Remove deleted destination from internal database > + stream->dest_names.erase(std::remove(stream->dest_names.begin(), > + stream->dest_names.end(), it), > + stream->dest_names.end()); > + } > + } else { > + // Delete all destination names > + stream->dest_names.clear(); > + } > + > + // Prepare stb_dest_names for checkingpoint to standby > + log_stream_form_dest_names(stream); > +} > + > +void log_stream_form_dest_names(log_stream_t* stream) { > + osafassert(stream != nullptr); > + std::string output{""}; > + bool addSemicolon = false; > + // Form stb_dest_names before checking point to standby > + // under format "name1;name2;etc". > + for (const auto& it : stream->dest_names) { > + output += it + ";"; > + addSemicolon = true; > + } > + if (addSemicolon == true) { > + osafassert(output.length() > 0); > + // Remove last semicolon(;) > + output[output.length() - 1] = '\0'; > + } > + stream->stb_dest_names = output; > +} > diff --git a/src/log/logd/lgs_stream.h b/src/log/logd/lgs_stream.h > --- a/src/log/logd/lgs_stream.h > +++ b/src/log/logd/lgs_stream.h > @@ -1,6 +1,7 @@ > /* -*- OpenSAF -*- > * > * (C) Copyright 2008 The OpenSAF Foundation > + * Copyright Ericsson AB 2014, 2017 - All Rights Reserved. [Lennart] Incorrect copyright year > * > * This program is distributed in the hope that it will be useful, but > * WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY > @@ -22,6 +23,7 @@ > #include "base/ncspatricia.h" > #include <time.h> > #include <limits.h> > +#include <vector> > > #include "lgs_fmt.h" > #include "base/osaf_extended_name.h" > @@ -83,6 +85,12 @@ typedef struct log_stream { > std::string stb_logFileCurrent; /* Current file name used on standby */ > std::string stb_prev_actlogFileCurrent; /* current file name on active when > previous record was written */ > uint32_t stb_curFileSize; /* Bytes written to current log file */ > + > + // Hold vector of destname string {"name1", "name2", etc.} > + std::vector<std::string> dest_names; > + // Hold a list of strings separated by semicolon "name1;name2;etc" > + // This data is used to checkpoint to standby > + std::string stb_dest_names; > } log_stream_t; > > extern uint32_t log_stream_init(); > @@ -133,4 +141,12 @@ void log_free_stream_resources(log_strea > log_stream_t *iterate_all_streams(SaBoolT &end, SaBoolT jstart); > extern log_stream_t *log_stream_get_by_name(const std::string &name); > > +void log_stream_add_dest_name(log_stream_t *stream, > + const std::vector<std::string>& names); > +void log_stream_replace_dest_name(log_stream_t *stream, > + const std::vector<std::string>& names); > +void log_stream_delete_dest_name(log_stream_t *stream, > + const std::vector<std::string>& names); > +void log_stream_form_dest_names(log_stream_t* stream); > + > #endif // LOG_LOGD_LGS_STREAM_H_ > diff --git a/src/log/logd/lgs_util.cc b/src/log/logd/lgs_util.cc > --- a/src/log/logd/lgs_util.cc > +++ b/src/log/logd/lgs_util.cc > @@ -835,3 +835,66 @@ bool lgs_is_extended_name_valid(const Sa > > return true; > } > + > +//======================================================== > ====================== > +// logutil namespace > +//======================================================== > ====================== > +namespace logutil { > + > +// Split a string @str with delimiter @delimiter > +// and return an vector of strings. > +std::vector<std::string> Parser(const std::string& str, > + const std::string& delimiter) { > + std::vector<std::string> temp; > + std::string s{str}; > + size_t pos = 0; > + while ((pos = s.find(delimiter)) != std::string::npos) { > + temp.push_back(s.substr(0, pos)); > + s.erase(0, pos + delimiter.length()); > + } > + temp.push_back(s); > + return temp; > +} > + > +bool isUnixName(const std::string& name) { > + int index = 0; > + char c; > + > + while ((c = name[index++]) != '\0') { > + switch (c) { > + case '/': > + case '|': > + case ';': > + case ',': > + case '!': > + case '@': > + case '#': > + case '$': > + case '(': > + case ')': > + case '<': > + case '>': > + case '\\': > + case '"': > + case '\'': > + case '`': > + case '~': > + case '{': > + case '}': > + case '[': > + case ']': > + case '+': > + case '&': > + case '^': > + case '?': > + case '*': > + case '%': > + return false; > + default: > + break; > + } > + } > + return true; > +} [Lennart] Seems complicated why not check allowed characters instead? Example (have not checked if this works) std::string x(/*Load*/); if (x.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_") != std::string::npos) { std::cerr << "Error\n"; } > + > +}; // namespace logutil > diff --git a/src/log/logd/lgs_util.h b/src/log/logd/lgs_util.h > --- a/src/log/logd/lgs_util.h > +++ b/src/log/logd/lgs_util.h > @@ -26,7 +26,7 @@ > > #include "osaf/saf/saAis.h" > #include "amf/saf/saAmf.h" > - > +#include <vector> > #include "lgs_stream.h" > #include "lgs_evt.h" > > @@ -83,4 +83,13 @@ void lgs_close_timer(int ufd); > > bool lgs_is_extended_name_valid(const SaNameT* name); > > +namespace logutil { > +// Parse string format "a;b;c" to vector of string {a, b, c} > +// In generic, this function could be used to split a string > +// separated by delimiter to a vector of strings. > +std::vector<std::string> Parser(const std::string&, const std::string&); > +// Check if @name contain special characters in. > +bool isUnixName(const std::string& name); > +}; // namespace logutil > + > #endif // LOG_LOGD_LGS_UTIL_H_ ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, SlashDot.org! http://sdm.link/slashdot _______________________________________________ Opensaf-devel mailing list Opensaf-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/opensaf-devel