Hi Lennart,

Ack with few comments. [Vu]

Regards, Vu

> -----Original Message-----
> From: Lennart Lund [mailto:lennart.l...@ericsson.com]
> Sent: Monday, February 20, 2017 10:18 PM
> To: mahesh.va...@oracle.com; vu.m.ngu...@dektech.com.au
> Cc: opensaf-devel@lists.sourceforge.net
> Subject: [PATCH 1 of 1] lgs: Add new multivalue attributes to
configuration
> handler [#2258]
> 
>  src/log/Makefile.am                      |    5 +-
>  src/log/apitest/imm_tstutil.c            |  126 +++++++
>  src/log/apitest/imm_tstutil.h            |   61 +++
>  src/log/apitest/logtest.h                |    2 +-
>  src/log/apitest/saflogtest.c             |   29 -
>  src/log/apitest/tet_LogOiOps.c           |  408 ++++++++++++++++++++++++
>  src/log/apitest/tet_Log_recov.c          |    7 +-
>  src/log/apitest/tet_log_runtime_cfgobj.c |    9 +-
>  src/log/config/logsv_classes.xml         |   27 +
>  src/log/logd/lgs.h                       |    2 +-
>  src/log/logd/lgs_config.cc               |  507
++++++++++++++++++++++++------
>  src/log/logd/lgs_config.h                |  252 ++++++++++++++-
>  src/log/logd/lgs_evt.cc                  |    2 +-
>  src/log/logd/lgs_file.cc                 |    2 +-
>  src/log/logd/lgs_imm.cc                  |   69 +++-
>  src/log/logd/lgs_mbcsv.cc                |    2 +-
>  src/log/logd/lgs_mbcsv_v5.cc             |    2 +
>  17 files changed, 1344 insertions(+), 168 deletions(-)
> 
> 
> Add new attributes to the configuration object and configuration info
object
> The new attributes are multi value so multi value support is also added
> 
> NOTE: UML test fixed and new test cases added
> 
> diff --git a/src/log/Makefile.am b/src/log/Makefile.am
> --- a/src/log/Makefile.am
> +++ b/src/log/Makefile.am
> @@ -1,6 +1,7 @@
>  #      -*- OpenSAF  -*-
>  #
>  # (C) Copyright 2016 The OpenSAF Foundation
> +# Copyright Ericsson AB [2016, 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
> @@ -147,7 +148,8 @@ bin_PROGRAMS += bin/logtest bin/saflogte
> 
>  noinst_HEADERS += \
>       src/log/apitest/logtest.h \
> -     src/log/apitest/logutil.h
> +     src/log/apitest/logutil.h \
> +     src/log/apitest/imm_tstutil.h
> 
>  bin_logtest_CFLAGS = $(AM_CFLAGS) -Wformat=1
> 
> @@ -158,6 +160,7 @@ bin_logtest_CPPFLAGS = \
>  bin_logtest_SOURCES = \
>       src/log/apitest/logtest.c \
>       src/log/apitest/logutil.c \
> +     src/log/apitest/imm_tstutil.c \
>       src/log/apitest/tet_saLogInitialize.c \
>       src/log/apitest/tet_saLogSelectionObjectGet.c \
>       src/log/apitest/tet_saLogDispatch.c \
> diff --git a/src/log/apitest/imm_tstutil.c b/src/log/apitest/imm_tstutil.c
> new file mode 100644
> --- /dev/null
> +++ b/src/log/apitest/imm_tstutil.c
> @@ -0,0 +1,126 @@
> +/*      -*- 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.
> + *
> + */
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdint.h>
> +#include "imm/saf/saImm.h"
> +#include "imm/saf/saImmOm.h"
> +#include "osaf/immutil/immutil.h"
> +#include "base/saf_error.h"
> +#include "base/osaf_extended_name.h"
> +
> +static SaVersionT immVersion = {'A', 2, 11};
> +
> +bool get_multivalue_type_string_from_imm(SaImmHandleT *omHandle,
> +                                     SaConstStringT objectName,
> +                                     char *attribute_name,
> +                                     char ***multivalue_array) {
> +     SaAisErrorT om_rc = SA_AIS_OK;
> +     SaImmAccessorHandleT accessorHandle;
> +     SaImmAttrValuesT_2 *attribute;
> +     SaImmAttrValuesT_2 **attributes;
> +     bool func_rc = true;
> +
> +     //printf(">> get_multivalue_string_type_from_imm()\n");
> +
> +     do {
> +             /* Make sure this is a NULL pointer if no values are found
*/
> +             *multivalue_array = NULL;
> +
> +             om_rc = immutil_saImmOmInitialize(omHandle, NULL,
> &immVersion);
> +             if (om_rc != SA_AIS_OK) {
> +                     printf("immutil_saImmOmInitialize Fail '%s'\n",
> +                             saf_error(om_rc));
> +                     func_rc = false;
> +                     break;
> +             }
> +
> +             //printf("\timmutil_saImmOmInitialize() Done\n");
> +
> +             om_rc = immutil_saImmOmAccessorInitialize(*omHandle,
> +                     &accessorHandle);
> +             if (om_rc != SA_AIS_OK) {
> +                     printf("immutil_saImmOmAccessorInitialize failed:
> %s\n",
> +                             saf_error(om_rc));
> +                     func_rc = false;
> +                     break;
> +             }
> +
> +             //printf("\timmutil_saImmOmAccessorInitialize() Done\n");
> +
> +             //SaConstStringT objectName =
> "logConfig=1,safApp=safLogService";
> +             SaNameT tmpObjName;
> +             osaf_extended_name_lend(objectName, &tmpObjName);
> +
> +             /* Get the attribute */
> +             SaImmAttrNameT attributeNames[2];
> +             attributeNames[0] = attribute_name;
> +             attributeNames[1] = NULL;
> +             om_rc = immutil_saImmOmAccessorGet_2(
> +                     accessorHandle,
> +                     &tmpObjName,
> +                     attributeNames,
> +                     &attributes);
> +             if (om_rc != SA_AIS_OK) {
> +                     printf("immutil_saImmOmAccessorGet_2 Fail '%s'\n",
> +                             saf_error(om_rc));
> +                     func_rc = false;
> +                     break;
> +             }
> +
> +             //printf("\timmutil_saImmOmAccessorGet_2() Done\n");
> +
> +             attribute = attributes[0];
> +             char **str_array = NULL;
> +
> +             /* Get values if there are any */
> +             if (attribute->attrValuesNumber > 0) {
> +                     size_t array_len = attribute->attrValuesNumber + 1;
> +                     str_array = (char **) calloc(array_len, sizeof(char
*));
> +                     str_array[array_len - 1] = NULL; /* NULL terminated
> array */
> +
> +                     /* Save values */
> +                     void *value = NULL;
> +                     for (uint32_t i = 0; i <
attribute->attrValuesNumber;
> +                             i++) {
> +                             value = attribute->attrValues[i];
> +                             str_array[i] = *(char **) value;
> +                     }
> +             }
> +
> +             *multivalue_array = str_array;
> +     } while (0);
> +
> +     //printf("<< get_multivalue_string_type_from_imm()\n");
> +     return func_rc;
> +}
> +
> +void free_multivalue(SaImmHandleT omHandle, char ***multivalue_array) {
> +     //printf(">> free_multivalue_array() ptr = %p\n",
*multivalue_array);
> +     if (*multivalue_array == NULL) {
> +             return;
> +     }
> +
> +     free(*multivalue_array);
> +
> +     SaAisErrorT rc = immutil_saImmOmFinalize(omHandle);
> +     if (rc != SA_AIS_OK) {
> +             printf("free_multivalue: immutil_saImmOmFinalize() Fail
> %s\n",
> +                     saf_error(rc));
> +     }
> +     //printf("<< free_multivalue_array()\n");
> +}
> +
> \ No newline at end of file
> diff --git a/src/log/apitest/imm_tstutil.h b/src/log/apitest/imm_tstutil.h
> new file mode 100644
> --- /dev/null
> +++ b/src/log/apitest/imm_tstutil.h
> @@ -0,0 +1,61 @@
> +/*      -*- 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.
> + *
> + */
> +
> +#include "log/apitest/logtest.h"
> +
> +#ifndef IMM_TSTUTIL_H
> +#define IMM_TSTUTIL_H
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * Handle reading of multivalue of string type from log service
configuration
> + * object
> + */
> +
> +/**
> + * Get one multi value of string type (SA_IMM_ATTR_SASTRINGT) from the
> + * "logConfig=1,safApp=safLogService" object
> + *
> + * See also get_attr_value() for other non multivalue attributes
> + *
> + * Note: Memory is allocated for the multivalue_array that has to be
freed
> + *       after usage. Use free_multivalue_array()
> + *
> + * @param attribute_name[in]
> + * @param multivalue_array[out] NULL terminated array of strings (char *)
> + * @return false if Fail. Fail message is printed on stdout
> + */
> +bool get_multivalue_type_string_from_imm(SaImmHandleT *omHandle,
> +                                     SaConstStringT objectName,
> +                                     char *attribute_name,
> +                                     char ***multivalue_array);
> +
> +/**
> + * Used to free memory allocated for the multivalue_array by function
> + * get_multivalue_string_type_from_imm()
> + *
> + * @param multivalue_array[in]
> + */
> +void free_multivalue(SaImmHandleT omHandle, char ***multivalue_array);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* IMM_TSTUTIL_H */
> +
> diff --git a/src/log/apitest/logtest.h b/src/log/apitest/logtest.h
> --- a/src/log/apitest/logtest.h
> +++ b/src/log/apitest/logtest.h
> @@ -124,7 +124,7 @@ static inline void time_meas_start(time_
>  static inline void time_meas_log(time_meas_t* tm, char *id) {
>    osaf_clock_gettime(CLOCK_REALTIME, &tm->end_time);
>    osaf_timespec_subtract(&tm->end_time, &tm->start_time, &tm-
> >diff_time);
> -  LOG_NO("LLDTEST3 %s [%s]\t Elapsed time %ld sec, %ld nsec",
> +  LOG_NO("%s [%s]\t Elapsed time %ld sec, %ld nsec",
>           __FUNCTION__, id,
>           tm->diff_time.tv_sec, tm->diff_time.tv_nsec);
>  }
> diff --git a/src/log/apitest/saflogtest.c b/src/log/apitest/saflogtest.c
> --- a/src/log/apitest/saflogtest.c
> +++ b/src/log/apitest/saflogtest.c
> @@ -46,35 +46,6 @@
>  #include "osaf/saf/saAis.h"
>  #include "log/saf/saLog.h"
> 
> -#if 1 /*LLDTEST1 Test inline functions */
> -#include "base/osaf_time.h"
> -#include "base/logtrace.h"
> -
> -typedef struct {
> -    struct timespec start_time;
> -    struct timespec end_time;
> -    struct timespec diff_time;
> -} time_meas_t;
> -/**
> - *
> - * @param tm[out]
> - */
> -static inline void time_meas_start(time_meas_t* tm)
> -{
> -    osaf_clock_gettime(CLOCK_REALTIME, &tm->start_time);
> -}
> -
> -
> -static inline void time_meas_log(time_meas_t* tm, char *id)
> -{
> -    osaf_clock_gettime(CLOCK_REALTIME, &tm->end_time);
> -    osaf_timespec_subtract(&tm->end_time, &tm->start_time, &tm-
> >diff_time);
> -    LOG_NO("LLDTEST3 %s [%s]\t Elapsed time %ld sec, %ld nsec",
> -         __FUNCTION__, id,
> -         tm->diff_time.tv_sec, tm->diff_time.tv_nsec);
> -}
> -#endif
> -
>  #define DEFAULT_FORMAT_EXPRESSION "@Cr @Ch:@Cn:@Cs
> @Cm/@Cd/@CY @Sv @Sl \"@Cb\""
>  #define DEFAULT_APP_LOG_REC_SIZE 150
>  #define DEFAULT_APP_LOG_FILE_SIZE 1024 * 1024
> diff --git a/src/log/apitest/tet_LogOiOps.c
b/src/log/apitest/tet_LogOiOps.c
> --- a/src/log/apitest/tet_LogOiOps.c
> +++ b/src/log/apitest/tet_LogOiOps.c
> @@ -1,6 +1,7 @@
>  /*      -*- OpenSAF  -*-
>   *
>   * (C) Copyright 2008 The OpenSAF Foundation
> + * Copyright Ericsson AB [2008, 2017] - All Rights Reserved
>   *
>   * This program is distributed in the hope that it will be useful, but
>   * WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY
> @@ -24,6 +25,7 @@
>  #include "base/saf_error.h"
> 
>  #include "logtest.h"
> +#include "log/apitest/imm_tstutil.h"
> 
>  #define MAX_DATA 256
>  #define opensaf_user "opensaf"
> @@ -1844,6 +1846,407 @@ void saLogOi_515(void)
>       rc_validate(WEXITSTATUS(rc), 1);
>  }
> 
> +/**
> + * Help function for test of logRecordDestinationConfiguration
> + *
> + * @param set_values[in] Array of values to compare
> + * @param num_values[in] Number of values in set_values array
> + * @return false if compare fail
> + */
> +static bool read_and_compare(SaConstStringT objectName,
> +                     char set_values[][MAX_DATA],
> +                     int num_values) {
> +     char **read_values;
> +     char* attr_name = "logRecordDestinationConfiguration";
> +     SaImmHandleT omHandle;
> +     bool result = true;
> +
> +     do {
> +             // Read the values in logRecordDestinationConfiguration
> from IMM
> +             bool rc =
> get_multivalue_type_string_from_imm(&omHandle,
> +
>       LOGTST_IMM_LOG_CONFIGURATION,
> +                                             attr_name,
> +                                             &read_values);
> +             if (rc == false) {
> +                     printf("Read values Fail\n");
> +                     result = false;
> +                     break;
> +             }
> +
> +             // Special case if no values
> +             if (num_values == 0) {
> +                     if (read_values == NULL) {
> +                             result = true;
> +                     } else {
> +                             result = false;
> +                     }
> +                     break;
> +             }
> +
> +             // Compare values
> +             // Note: the order of the read values is not guaranteed
> meaning
> +             //       that it must be checked if any of the read values
> +             //       match a set value
> +             bool match = false;
> +             for (int i = 0; i < num_values; i++) {
> +                     for (int j = 0; j < num_values; j++) {
> +                             if (read_values[i] == NULL) {
> +                                     match = false;
> +                                     break;
> +                             }
> +                             if (strcmp(read_values[i], set_values[j]) ==
0)
> {
> +                                     match = true;
> +                                     break;
> +                             }
> +                     }
> +                     if (match == false) {
> +                             result = false;
> +                             break;
> +                     }
> +             }
> +
> +             free_multivalue(omHandle, &read_values);
> +     } while(0);
> +
> +     return result;
> +}
> +
> +/**
> + * Test adding values. Check configuration object and current config
object
> + */
> +void check_logRecordDestinationConfigurationAdd(void) {
> +     char command[MAX_DATA];
> +     const int num_values = 5;
> +     char set_values[num_values][MAX_DATA];
> +     int test_result = 0; /* -1 if Fail */
> +
> +     do {
> +             // Add values
> +             for (int i = 0; i < num_values; i++) {
> +                     sprintf(set_values[i], "Name%d;Type%d;Setting%d",
> i, i, i);
> +                     sprintf(command, "immcfg "
> +                             "-a logRecordDestinationConfiguration+="
> +                             "'%s' "
> +                             "logConfig=1,safApp=safLogService 2>
> /dev/null",
> +                             set_values[i]);
> +                     int rc = system(command);
> +                     if (rc != 0) {
> +                             printf("Set values Fail\n");
> +                             test_result = 1;
> +                             break;
> +                     }
> +             }
> +
> +             // Check that all added values exist
> +             bool result =
> read_and_compare(LOGTST_IMM_LOG_CONFIGURATION,
> +                                             set_values, num_values);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check OpenSafLogConfig Fail\n");
> +                     break;
> +             }
> +
> +             // Same check for current config object
> +             result = read_and_compare(LOGTST_IMM_LOG_RUNTIME,
> +                                     set_values, num_values);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check OpenSafLogCurrentConfig Fail\n");
> +                     break;
> +             }
> +     } while(0);
> +
> +     // Cleanup by removing all values
> +     sprintf(command, "immcfg -a logRecordDestinationConfiguration=''
> "
> +             "logConfig=1,safApp=safLogService 2> /dev/null");
> +     int rc = system(command);
> +     if (rc != 0) {
> +             printf("Cleanup Failed\n");
> +     }
> +
> +     rc_validate(test_result, 0);
> +}
> +
> +/**
> + * Test deleting values. Check configuration object and current config
> object
> + */
> +void check_logRecordDestinationConfigurationDelete(void) {
> +     char command[MAX_DATA];
> +     const int num_values = 5;
> +     char set_values[num_values][MAX_DATA];
> +     int test_result = 0; /* -1 if Fail */
> +
> +     do {
> +             // Add values
> +             for (int i = 0; i < num_values; i++) {
> +                     sprintf(set_values[i], "Name%d;Type%d;Setting%d",
> i, i, i);
> +                     sprintf(command, "immcfg "
> +                             "-a logRecordDestinationConfiguration+="
> +                             "'%s' "
> +                             "logConfig=1,safApp=safLogService 2>
> /dev/null",
> +                             set_values[i]);
> +                     int rc = system(command);
> +                     if (rc != 0) {
> +                             printf("Add values Fail\n");
> +                             test_result = 1;
> +                             break;
> +                     }
> +             }
> +
> +             // Delete last 2 values
> +             for (int i = num_values - 2; i < num_values; i++) {
> +                     sprintf(command, "immcfg "
> +                             "-a logRecordDestinationConfiguration-="
> +                             "'%s' "
> +                             "logConfig=1,safApp=safLogService 2>
> /dev/null",
> +                             set_values[i]);
> +                     int rc = system(command);
> +                     if (rc != 0) {
> +                             printf("Delete values Fail\n");
> +                             test_result = 1;
> +                             break;
> +                     }
> +             }
> +
> +             // Check that all not deleted values exist
> +             bool result =
> read_and_compare(LOGTST_IMM_LOG_CONFIGURATION,
> +                                             set_values, num_values - 2);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check values not deleted Fail\n");
> +                     break;
> +             }
> +
> +             // Check that deleted values do not exist
> +             result =
> read_and_compare(LOGTST_IMM_LOG_CONFIGURATION,
> +                                             set_values, num_values);
> +             if (result == true) {
> +                     test_result = 1;
> +                     printf("Check values deleted Fail\n");
> +                     break;
> +             }
> +
> +             // Same checks for current config object
> +             result = read_and_compare(LOGTST_IMM_LOG_RUNTIME,
> +                                             set_values, num_values - 2);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check values not deleted Fail\n");
> +                     break;
> +             }
> +
> +             result = read_and_compare(LOGTST_IMM_LOG_RUNTIME,
> +                                             set_values, num_values);
> +             if (result == true) {
> +                     test_result = 1;
> +                     printf("Check values deleted Fail\n");
> +                     break;
> +             }
> +     } while(0);
> +
> +     // Cleanup by removing all values
> +     sprintf(command, "immcfg -a logRecordDestinationConfiguration=''
> "
> +             "logConfig=1,safApp=safLogService 2> /dev/null");
> +     int rc = system(command);
> +     if (rc != 0) {
> +             printf("Cleanup Failed\n");
> +     }
> +
> +     rc_validate(test_result, 0);
> +}
> +
> +/**
> + * Test replacing values. Check configuration object and current config
> object
> + */
> +void check_logRecordDestinationConfigurationReplace(void) {
> +     char command[MAX_DATA*2];
> +     const int num_values = 5;
> +     char set_values[num_values][MAX_DATA];
> +     int test_result = 0; /* 1 if Fail */
> +
> +     do {
> +             // Add values that will be replaced
> +             for (int i = 0; i < num_values; i++) {
> +                     sprintf(set_values[i], "Name%d;Type%d;Setting%d",
> i, i, i);
> +                     sprintf(command, "immcfg "
> +                             "-a logRecordDestinationConfiguration+="
> +                             "'%s' "
> +                             "logConfig=1,safApp=safLogService 2>
> /dev/null",
> +                             set_values[i]);
> +                     int rc = system(command);
> +                     if (rc != 0) {
> +                             printf("Add values Fail\n");
> +                             test_result = 1;
> +                             break;
> +                     }
> +             }
> +
> +             // Check that values exist
> +             bool result =
> read_and_compare(LOGTST_IMM_LOG_CONFIGURATION,
> +                                             set_values, num_values);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check OpenSafLogConfig Fail\n");
> +                     break;
> +             }
> +
> +             // Replace the values
> +             int num_new_values = 3;
> +             for (int i = 0; i < num_new_values; i++) {
> +                     sprintf(set_values[i],
> +                             "NewName%d;NewType%d;NewSetting%d",
> i, i, i);
> +             }
> +             sprintf(command, "immcfg "
> +                     "-a logRecordDestinationConfiguration='%s' "
> +                     "-a logRecordDestinationConfiguration='%s' "
> +                     "-a logRecordDestinationConfiguration='%s' "
> +                     "logConfig=1,safApp=safLogService 2> /dev/null",
> +                     set_values[0], set_values[1], set_values[2]);
> +             int rc = system(command);
> +             if (rc != 0) {
> +                     printf("Add values Fail\n");
> +                     test_result = 1;
> +                     break;
> +             }
> +
> +             // Check that new values exist in configuration object
> +             result =
> read_and_compare(LOGTST_IMM_LOG_CONFIGURATION,
> +                                             set_values,
> num_new_values);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check OpenSafLogConfig Fail\n");
> +                     break;
> +             }
> +
> +             // Check that new values exist in current config object
> +             result = read_and_compare(LOGTST_IMM_LOG_RUNTIME,
> +                                     set_values, num_new_values);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check OpenSafLogCurrentConfig Fail\n");
> +                     break;
> +             }
> +
> +             // Check that no old values exist in current config object
> +             result = read_and_compare(LOGTST_IMM_LOG_RUNTIME,
> +                                     set_values, num_new_values+1);
> +             if (result == true) {
> +                     test_result = 1;
> +                     printf("Check OpenSafLogCurrentConfig Fail\n");
> +                     break;
> +             }
> +
> +     } while(0);
> +
> +     // Cleanup by removing all values
> +     sprintf(command, "immcfg -a logRecordDestinationConfiguration=''
> "
> +             "logConfig=1,safApp=safLogService 2> /dev/null");
> +     int rc = system(command);
> +     if (rc != 0) {
> +             printf("Cleanup Failed\n");
> +     }
> +
> +     rc_validate(test_result, 0);
> +}
> +
> +/**
> + * Test to clear all values (make <empty>
> + */
> +void check_logRecordDestinationConfigurationEmpty(void) {
> +     char command[MAX_DATA*2];
> +     const int num_values = 5;
> +     char set_values[num_values][MAX_DATA];
> +     int test_result = 0; /* 1 if Fail */
> +
> +     do {
> +             // Add values that will be replaced
> +             for (int i = 0; i < num_values; i++) {
> +                     sprintf(set_values[i], "Name%d;Type%d;Setting%d",
> i, i, i);
> +                     sprintf(command, "immcfg "
> +                             "-a logRecordDestinationConfiguration+="
> +                             "'%s' "
> +                             "logConfig=1,safApp=safLogService 2>
> /dev/null",
> +                             set_values[i]);
> +                     int rc = system(command);
> +                     if (rc != 0) {
> +                             printf("Add values Fail\n");
> +                             test_result = 1;
> +                             break;
> +                     }
> +             }
> +
> +             // Check that values exist
> +             bool result =
> read_and_compare(LOGTST_IMM_LOG_CONFIGURATION,
> +                                             set_values, num_values);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Check OpenSafLogConfig Fail\n");
> +                     break;
> +             }
> +
> +             // Clear the values
> +             sprintf(command, "immcfg "
> +                     "-a logRecordDestinationConfiguration='' "
> +                     "logConfig=1,safApp=safLogService 2> /dev/null");
> +             int rc = system(command);
> +             if (rc != 0) {
> +                     printf("Clear values Fail\n");
> +                     test_result = 1;
> +                     break;
> +             }
> +
> +             // Check that new values cleared in configuration object
> +             result =
> read_and_compare(LOGTST_IMM_LOG_CONFIGURATION,
> +                                             set_values, 0);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Clear OpenSafLogConfig Fail\n");
> +                     break;
> +             }
> +
> +             // Check that new values cleared in current config object
> +             result = read_and_compare(LOGTST_IMM_LOG_RUNTIME,
> +                                     set_values, 0);
> +             if (result == false) {
> +                     test_result = 1;
> +                     printf("Clear OpenSafLogCurrentConfig Fail\n");
> +                     break;
> +             }
> +
> +     } while(0);
> +
> +     // Cleanup by removing all values
> +     sprintf(command, "immcfg -a logRecordDestinationConfiguration=''
> "
> +             "logConfig=1,safApp=safLogService 2> /dev/null");
> +     int rc = system(command);
> +     if (rc != 0) {
> +             printf("Cleanup Failed\n");
> +     }
> +
> +     rc_validate(test_result, 0);
> +}
> +
> +/**
> + * Test setting an invalid value
> + */
> +void check_logRecordDestinationConfigurationInvalid(void) {
> +     char command[MAX_DATA];
> +     int test_result = 0; /* 1 if Fail */
> +
> +     // Set invalid value
> +     sprintf(command, "immcfg "
> +     "-a logRecordDestinationConfiguration+="
> +     "'Invalid value' "
> +     "logConfig=1,safApp=safLogService 2> /dev/null");
> +
> +     int rc = system(command);
> +     if (rc != 0) {
> +             test_result = 1;
> +     }
> +
> +     rc_validate(test_result, 1);
> +}
> +
>  /*
> ==============================================================
> ===============
>  * Test stream configuration object attribute validation, suite 6
>  * Note:
> @@ -4317,6 +4720,11 @@ done:
>       test_case_add(5, saLogOi_514, "CCB Object Modify,
> logMaxApplicationStreams. Not allowed");
>       test_case_add(5, saLogOi_515, "CCB Object Modify,
> logFileSysConfig. Not allowed");
>       test_case_add(5, change_root_path, "CCB Object Modify, change
> root directory. Path exist. OK");
> +     test_case_add(5, check_logRecordDestinationConfigurationAdd,
> "Add logRecordDestinationConfiguration. OK");
> +     test_case_add(5, check_logRecordDestinationConfigurationDelete,
> "Delete logRecordDestinationConfiguration. OK");
> +     test_case_add(5, check_logRecordDestinationConfigurationReplace,
> "Replace logRecordDestinationConfiguration. OK");
> +     test_case_add(5, check_logRecordDestinationConfigurationEmpty,
> "Clear logRecordDestinationConfiguration. OK");
> +     test_case_add(5, check_logRecordDestinationConfigurationInvalid,
> "Invalid logRecordDestinationConfiguration. ERR");
> 
>       /* Add test cases to test #1288 */
>       test_case_add(5, verLogFileIoTimeout, "CCB Object Modify:
> logFileIoTimeout is in range [500 - 5000], OK");
> diff --git a/src/log/apitest/tet_Log_recov.c
> b/src/log/apitest/tet_Log_recov.c
> --- a/src/log/apitest/tet_Log_recov.c
> +++ b/src/log/apitest/tet_Log_recov.c
> @@ -1,6 +1,7 @@
>  /*      -*- OpenSAF  -*-
>   *
>   * (C) Copyright 2015 The OpenSAF Foundation
> + * Copyright Ericsson AB [2015, 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
> @@ -853,7 +854,7 @@ typedef struct {
>   */
>  static void log_write_callback(SaInvocationT invocation, SaAisErrorT
error)
>  {
> -     printf_v(">> LLDTEST %s\n",__FUNCTION__);
> +     printf_v(">> %s\n",__FUNCTION__);
>       if (error != SA_AIS_OK) {
>               fprintf(stderr,"\t%s: error = %s\n",__FUNCTION__,
> saf_error(error));
>       }
> @@ -872,7 +873,7 @@ static void log_write_callback(SaInvocat
>       lgt_cb.ais_errno = error;
>       lgt_cb.invocation_out = invocation;
>       osaf_mutex_unlock_ordie(&write_mutex);
> -     printf_v("<< LLDTEST %s\n",__FUNCTION__);
> +     printf_v("<< %s\n",__FUNCTION__);
>  }
> 
>  /**
> @@ -1355,7 +1356,7 @@ void saLogRecov_prepare_client1_8streams
> 
>       rc = tst_StreamOpen_app_logtest_sc(g_client[0].glob_logHandle,
>               g_client[0].glob_logStreamHandle,
> -             8); /* LLDTEST shall be 8 */
> +             8);
>       if (rc != 0) {
>               fprintf(stderr, "\t%s Failed to open log stream\n",
>                       __FUNCTION__);
> diff --git a/src/log/apitest/tet_log_runtime_cfgobj.c
> b/src/log/apitest/tet_log_runtime_cfgobj.c
> --- a/src/log/apitest/tet_log_runtime_cfgobj.c
> +++ b/src/log/apitest/tet_log_runtime_cfgobj.c
> @@ -1,6 +1,7 @@
>  /*      -*- OpenSAF  -*-
>   *
>   * (C) Copyright 2015 The OpenSAF Foundation
> + * Copyright Ericsson AB [2015, 2017] - All Rights Reserved
>   *
>   * This program is distributed in the hope that it will be useful, but
>   * WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY
> @@ -27,7 +28,9 @@ static SaVersionT immVersion = { 'A', 2,
>   * Log configuration config obj <=> runtime obj
>   *
>   * Verfy that the configuration and runtime object for log server
> configuration
> - * data contains the same number of attributes
> + * data contains the same number of attributes.
> + * Note: The runtime object contains 1 more attribute than the
> configuration
> + *       object
>   */
>  void log_rt_cf_obj_compare(void)
>  {
> @@ -75,8 +78,10 @@ void log_rt_cf_obj_compare(void)
>       r_cnt = 0;
>       while (attributes[r_cnt++] != NULL); /* Count the attributes */
> 
> -     /* Compare number of attributes. Test pass if the same number
> +     /* Compare number of attributes. Test pass if the runtime object has
> +      * 1 more attribute than the configuration object
>        */
> +     r_cnt--;
>       if (c_cnt != r_cnt) {
>               tst_res = 1;
>               fprintf(stderr, "Found %d configuration attributes and"
> 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
> @@ -148,6 +148,13 @@
>                       <flag>SA_WRITABLE</flag>
>               </attr>
>               <attr>
> +                     <name>saLogRecordDestination</name>
> +                     <type>SA_UINT32_T</type>
> +                     <category>SA_CONFIG</category>
> +                     <flag>SA_WRITABLE</flag>
> +                        <flag>SA_MULTI_VALUE</flag>
> +             </attr>
> +             <attr>
>                       <name>saLogStreamCreationTimestamp</name>
>                       <type>SA_TIME_T</type>
>                       <category>SA_RUNTIME</category>
> @@ -250,6 +257,14 @@ to ensure that default global values in
>                       <category>SA_CONFIG</category>
>                       <flag>SA_WRITABLE</flag>
>               </attr>
> +             <attr>
> +                     <name>logRecordDestinationConfiguration</name>
> +                     <type>SA_STRING_T</type>
> +                     <category>SA_CONFIG</category>
> +                     <flag>SA_WRITABLE</flag>
> +                        <flag>SA_MULTI_VALUE</flag>
> +                        <flag>SA_NO_DUPLICATES</flag>
> +             </attr>
>       </class>
>       <class name="OpenSafLogCurrentConfig">
>               <category>SA_RUNTIME</category>
> @@ -314,5 +329,17 @@ to ensure that default global values in
>                       <type>SA_STRING_T</type>
>                       <category>SA_RUNTIME</category>
>               </attr>
> +             <attr>
> +                     <name>logRecordDestinationConfiguration</name>
> +                     <type>SA_STRING_T</type>
> +                     <category>SA_RUNTIME</category>
> +                        <flag>SA_MULTI_VALUE</flag>
> +             </attr>
> +             <attr>
> +                     <name>logRecordDestinationStatus</name>
> +                     <type>SA_STRING_T</type>
> +                     <category>SA_RUNTIME</category>
> +                        <flag>SA_MULTI_VALUE</flag>
> +             </attr>
>       </class>
>  </imm:IMM-contents>
> diff --git a/src/log/logd/lgs.h b/src/log/logd/lgs.h
> --- a/src/log/logd/lgs.h
> +++ b/src/log/logd/lgs.h
> @@ -74,7 +74,7 @@
>  /* The name of log service config object */
>  #define LGS_IMM_LOG_CONFIGURATION
> "logConfig=1,safApp=safLogService"
> 
> -/* The possible configurations for LGS_IMM_LOG_FILESYS_CFG */
> +/* The possible configurations for LGS_IMM_LOG_FILE_SYS_CONFIG */
>  #define LGS_LOG_SHARED_FILESYSTEM 1             /* Use shared filesystem.
> Default */
>  #define LGS_LOG_SPLIT_FILESYSTEM  2     /* Store logs on local file
system
> on
>                                             each node */
> 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
> @@ -1,6 +1,7 @@
>  /*      -*- OpenSAF  -*-
>   *
>   * (C) Copyright 2015 The OpenSAF Foundation
> + * Copyright Ericsson AB [2015, 2017] - All Rights Reserved
>   *
>   * This program is distributed in the hope that it will be useful, but
>   * WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY
> @@ -27,6 +28,10 @@
>  #include <string.h>
>  #include <utmp.h>
> 
> +#include <string>
> +#include <vector>
> +#include <algorithm>
> +
>  #include "osaf/configmake.h"
>  #include "base/saf_error.h"
>  #include "base/osaf_secutil.h"
> @@ -41,15 +46,6 @@ static SaVersionT immVersion = { 'A', 2,
>  /* Mutex for making read and write of configuration data thread safe */
>  pthread_mutex_t lgs_config_data_mutex = PTHREAD_MUTEX_INITIALIZER;
> 
> -/***
> - * This file contains handling of Log service configuration including:
> - *  - Configuration object
> - *  - Environment variables
> - *  - Default values
> - *  - Verification of attribute values
> - *  - Check-pointing
> - */
> -
>  /* The name of log service config object */
>  #define LGS_IMM_LOG_CONFIGURATION
> "logConfig=1,safApp=safLogService"
> 
> @@ -118,8 +114,13 @@ typedef struct _lgs_conf_t {
>    SaUint32T logMaxApplicationStreams;
>    SaUint32T logFileIoTimeout;
>    SaUint32T logFileSysConfig;
> +  std::vector<std::string> logRecordDestinationConfiguration; // Default
> empty
>    /* --- end correspond to IMM Class --- */
> 
> +  /* --- Used with OpenSafLogCurrentConfig runtime object only --- */
> +  /* Note: Has no cnfflag */
> +  std::vector<std::string> logRecordDestinationStatus; // Default empty
> +
>    /* Used for checkpointing time when files are closed */
>    time_t chkp_file_close_time;
> 
> @@ -136,20 +137,22 @@ typedef struct _lgs_conf_t {
>    lgs_conf_flg_t logFileSysConfig_cnfflag;
>    lgs_conf_flg_t logDataGroupname_cnfflag;
>    lgs_conf_flg_t logStreamFileFormat_cnfflag;
> +  lgs_conf_flg_t logRecordDestinationConfiguration_cnfflag;
> 
>    _lgs_conf_t() :
> -    logRootDirectory(lgs_conf_def.logRootDirectory),
> -    logRootDirectory_cnfflag(LGS_CNF_DEF),
> -    logMaxLogrecsize_cnfflag(LGS_CNF_DEF),
> -    logStreamSystemHighLimit_cnfflag(LGS_CNF_DEF),
> -    logStreamSystemLowLimit_cnfflag(LGS_CNF_DEF),
> -    logStreamAppHighLimit_cnfflag(LGS_CNF_DEF),
> -    logStreamAppLowLimit_cnfflag(LGS_CNF_DEF),
> -    logMaxApplicationStreams_cnfflag(LGS_CNF_DEF),
> -    logFileIoTimeout_cnfflag(LGS_CNF_DEF),
> -    logFileSysConfig_cnfflag(LGS_CNF_DEF),
> -    logDataGroupname_cnfflag(LGS_CNF_DEF),
> -    logStreamFileFormat_cnfflag(LGS_CNF_DEF) {
> +    logRootDirectory {PKGLOGDIR},
> +    logRootDirectory_cnfflag {LGS_CNF_DEF},
> +    logMaxLogrecsize_cnfflag {LGS_CNF_DEF},
> +    logStreamSystemHighLimit_cnfflag {LGS_CNF_DEF},
> +    logStreamSystemLowLimit_cnfflag {LGS_CNF_DEF},
> +    logStreamAppHighLimit_cnfflag {LGS_CNF_DEF},
> +    logStreamAppLowLimit_cnfflag {LGS_CNF_DEF},
> +    logMaxApplicationStreams_cnfflag {LGS_CNF_DEF},
> +    logFileIoTimeout_cnfflag {LGS_CNF_DEF},
> +    logFileSysConfig_cnfflag {LGS_CNF_DEF},
> +    logDataGroupname_cnfflag {LGS_CNF_DEF},
> +    logStreamFileFormat_cnfflag {LGS_CNF_DEF},
> +    logRecordDestinationConfiguration_cnfflag {LGS_CNF_DEF} {
>      OpenSafLogConfig_object_exist = false;
>      /*
>       * The following attributes cannot be configured in the config file
> @@ -170,41 +173,10 @@ typedef struct _lgs_conf_t {
> 
>  static lgs_conf_t lgs_conf;
> 
> -
> /*************************************************************
> *****************
> - * Internal functions
> -
> **************************************************************
> ****************/
> -
>  static char *cnfflag_str(lgs_conf_flg_t cnfflag);
>  static int verify_all_init();
> 
> -
> /*************************************************************
> *****************
> - * Utility functions
> - */
> 
> -
> /*************************************************************
> *****************
> - * Check-pointing handling of configuration
> - */
> -
> -/**
> - * Create configuration data buffer
> - * Creates a buffer containing all configuration data to be updated.
> - * Each time the function is called a configuration parameter is added.
> - *
> - * The buffer structure can be sent as a check-point message and the
> updating
> - * is done on the Standby. It can also be used in the OI to create an
update
> - * buffer in the apply callback. Use lgs_cfg_update() for updating the
> - * configuration.
> - *
> - * NOTE1: Parameter name and value is not validated
> - * NOTE2: This function allocates memory pointed to by config_data in the
> - *        lgs_config_chg_t structure. This memory has to bee freed after
usage
> - *
> - * @param name_str[in]  String containing the parameter name
> - * @param value_str[in] Parmeter value as a string
> - * @param config_data[out] Filled in config data structure
> - *                         NOTE! Must be initiated before first call
{NULL, 0}
> - *
> - */
>  void lgs_cfgupd_list_create(const char *name_str, char *value_str,
>                              lgs_config_chg_t *config_data) {
>    char *tmp_char_ptr = NULL;
> @@ -241,7 +213,7 @@ void lgs_cfgupd_list_create(const char *
>      tmp_char_ptr = static_cast<char *>(realloc(
>          config_data->ckpt_buffer_ptr, alloc_size));
>      if (tmp_char_ptr == NULL) {
> -      TRACE("%s: malloc Fail Aborted", __FUNCTION__);
> +      LOG_ER("%s: malloc Fail Aborted", __FUNCTION__);
>        osaf_abort(0);
>      }
> 
> @@ -256,27 +228,120 @@ void lgs_cfgupd_list_create(const char *
>    TRACE_LEAVE();
>  }
> 
> +/************************************************************
> *******************
> + * Help functions for handling multi value attributes with the cfgupd
list.
> + * Multi values can be handled in three ways Add, Delete and Replace
> + * In the configuration handler multi values will always be replaced.
These
> + * help functions will always create a complete list of values and add
them
> + * to the cfgupd list. Multiple values will be added to the list by
adding
> + * the same attribute multiple times with different values.
> + * When reading a list using lgs_cfgupd_list_read() a multi value will be
> given
> + * by returning the same attribute multiple times with different values
> + *
> + * All functions takes the following parameters:
> + *
> + * @param attribute_name:
> + * The name of the multi value attribute
> + *
> + * @param value_list:
> + * A list of strings where each string represents a value
> + *
> + * @param config_data
> + * See lgs_cfgupd_list_create()
> + */
> +
> +void lgs_cfgupd_multival_add(const std::string& attribute_name,
> +                             const std::vector<std::string>& value_list,
> +                             lgs_config_chg_t *config_data) {
> +  TRACE_ENTER();
> +  // Get the existing multi-values and add them to the config data list
> +  lgs_logconfGet_t param_id = param_name_to_id(attribute_name);
> +  const std::vector<std::string> *exist_list =
> +      reinterpret_cast<const
> std::vector<std::string>*>(lgs_cfg_get(param_id));
> +
> +  for (auto& value : *exist_list) {
[Vu] Consider to use "const auto&" for read-only values.

> +    lgs_cfgupd_list_create(attribute_name.c_str(),
> +                           const_cast<char *>(value.c_str()),
> +                           config_data);
> +  }
> +
> +  // Add the new values in the value-list to the config data list
> +  for (auto& value : value_list) {
[Vu] Consider to use "const auto&" for read-only values.
> +    lgs_cfgupd_list_create(attribute_name.c_str(),
> +                           const_cast<char *>(value.c_str()),
> +                           config_data);
> +  }
> +  TRACE_LEAVE();
> +}
> +
>  /**
> - * Read a config update buffer and get parameter name and value.
> - * The first time the function is called next_param_ptr shall be
> - * a pointer to the buffer containing the config data.
> - * See ckpt_buffer_ptr in lgs_config_chg_t.
> - * To get the next parameter the next_param_ptr[in] shall be set to the
> - * return value from the previous call. NULL is returned when the last
> parameter
> - * is read.
> - * The last parameter cfgupd_ptr shall be a pointer to the buffer
structure to
> - * read
> - *
> - * NOTE: The string pointed to by cfgupd_ptr->ckpt_buffer_ptr is changed
in
> - *       this function! See strtok_r()
> - *
> - * @param name_str[out]
> - * @param value_str[out]
> - * @param next_param_ptr[in]
> - * @param cfgupd_ptr[in]
> - *
> - * @return next_param_ptr
> + * Delete the values given in the list from the multi value attribute
> + *
>   */
> +static bool is_value_in_vector(const std::vector<std::string>&
> search_vector,
> +                        const std::string& searched_value) {
> +  // Check if value is in vector
> +  bool rc = false;
> +  for (auto& value : search_vector) {
[Vu] Consider to use "const auto&" for read-only values.
> +    if (value == searched_value) {
> +      rc = true;
> +      break;
> +    }
> +  }
> +  return rc;
> +}
> +void lgs_cfgupd_multival_delete(std::string attribute_name,
> +                                std::vector<std::string> value_list,
> +                                lgs_config_chg_t *config_data) {
[Vu] Consider to use constant and reference type for read-only class
parameters (e.g: const std::string&, const std::vector<std::string>&
value_list)
> +  TRACE_ENTER();
> +  // Get the existing multi-values
> +  lgs_logconfGet_t param_id = param_name_to_id(attribute_name);
> +  const std::vector<std::string> *exist_list =
> +      reinterpret_cast<const
> std::vector<std::string>*>(lgs_cfg_get(param_id));
> +
> +  // Iterate over the exist_list and create a new list containing the
> +  // existing values except the values in the given value-list
> +  std::vector<std::string> result_list;
> +  for (auto& exist_value: *exist_list)
 {
> +    if (is_value_in_vector(value_list, exist_value) == false) {
> +      result_list.push_back(exist_value);
> +    }
> +  }
> +
> +  // Add this new list to the config data list
> +  for (auto& value : result_list) {
> +    lgs_cfgupd_list_create(attribute_name.c_str(),
> +                           const_cast<char *>(value.c_str()),
> +                           config_data);
> +  }
> +  TRACE_LEAVE();
> +}
> +
> +/**
> + * Replace all existing values in the multi value attribute with the
values in
> + * the list
> + */
> +void lgs_cfgupd_mutival_replace(std::string attribute_name,
> +                                std::vector<std::string> value_list,
> +                                lgs_config_chg_t *config_data) {
[Vu] Consider to use constant and reference type for read-only class
parameters (e.g: const std::string&, const std::vector<std::string>&
value_list)
> +  TRACE_ENTER();
> +
> +  // Add given value-list to the config data list
> +  if (value_list.empty()) {
> +    // Special case. Create config_data with empty value
> +    lgs_cfgupd_list_create(attribute_name.c_str(), const_cast<char
*>(""),
> +                           config_data);
> +  } else {
> +    for (auto& value : value_list) {
> +      lgs_cfgupd_list_create(attribute_name.c_str(),
> +                             const_cast<char *>(value.c_str()),
> +                             config_data);
> +    }
> +  }
> +
> +  TRACE_LEAVE();
> +}
> +
>  char *lgs_cfgupd_list_read(char **name_str, char **value_str,
>                             char *next_param_ptr, lgs_config_chg_t
*cfgupd_ptr) {
>    char *bufend_ptr = NULL;
> @@ -306,23 +371,6 @@ done:
>    return next_ptr;
>  }
> 
> -/**
> - * Parse a configuration data buffer and update the configuration
structure.
> - * Used on Standby when receiving configuration check-point data and by
OI
> - * to update configuration at modify apply.
> - *
> - * NOTE1: Operates on a static data structure. Is not thread safe
> - * NOTE2: Validation is done here. If validation fails we will be out of
> - *        sync. A warning is logged to syslog
> - * Comment: Check-pointed configuration data is always sent when the
> - *          configuration object is updated or when applying cahnges in
IO.
> - *          This means that the corresponding cnfflag is set to
LGS_CNF_OBJ
> - *
> - * @param config_data[in] Pointer to structure containing configuration
> - *        data buffer
> - *
> - * @return -1 if validation error
> - */
>  int lgs_cfg_update(const lgs_config_chg_t *config_data) {
>    char *bufend_ptr = NULL;
>    char *allocmem_ptr = NULL;
> @@ -332,6 +380,8 @@ int lgs_cfg_update(const lgs_config_chg_
>    char *value_str = NULL;
>    char *saveptr = NULL;
>    int rc = 0;
> +  bool logRecordDestinationConfiguration_list_clear = true;
> +  bool logRecordDestinationStatus_list_clear = true;
> 
>    TRACE_ENTER();
>    /* Validate config_data */
> @@ -403,6 +453,28 @@ int lgs_cfg_update(const lgs_config_chg_
>      } else if (strcmp(name_str, LOG_FILE_SYS_CONFIG) == 0) {
>        lgs_conf.logFileSysConfig = (SaUint32T)
>            strtoul(value_str, NULL, 0);
> +    } else if (strcmp(name_str,
> LOG_RECORD_DESTINATION_CONFIGURATION) == 0) {
> +      if (logRecordDestinationConfiguration_list_clear) {
> +        lgs_conf.logRecordDestinationConfiguration.clear();
> +        logRecordDestinationConfiguration_list_clear = false;
> +      }
> +      if (strlen(value_str) == 0) {
> +        // The attribute has no values
> +        lgs_conf.logRecordDestinationConfiguration.clear();
> +      } else {
> +        lgs_conf.logRecordDestinationConfiguration.push_back(value_str);
> +      }
> +    } else if (strcmp(name_str, LOG_RECORD_DESTINATION_STATUS) == 0) {
> +      if (logRecordDestinationStatus_list_clear) {
> +        lgs_conf.logRecordDestinationStatus.clear();
> +        logRecordDestinationStatus_list_clear = false;
> +      }
> +      if (strlen(value_str) == 0) {
> +        // The attribute has no values
> +        lgs_conf.logRecordDestinationStatus.clear();
> +      } else {
> +        lgs_conf.logRecordDestinationStatus.push_back(value_str);
> +      }
>      }
> 
>      param_ptr = next_ptr;
> @@ -413,8 +485,6 @@ int lgs_cfg_update(const lgs_config_chg_
>    /* Config data is written. Mutex can be unlocked */
>    osaf_mutex_unlock_ordie(&lgs_config_data_mutex);
> 
> -  lgs_trace_config();
> -
>    /* Validate the configuration structure data
>     * For the moment Log a warning and do nothing.
>     * NOTE: Configuration that's failed is replaced by default values
> @@ -639,6 +709,35 @@ static int lgs_cfg_verify_log_filesys_co
>    return rc;
>  }
> 
> +/**
> + * 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
> + *
> + * @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;
> +}
> +
> 
>  /**
>   * Verify logRootDirectory; path to be used as log root directory
> @@ -745,6 +844,13 @@ static int verify_all_init() {
>      lgs_conf.logFileSysConfig_cnfflag = LGS_CNF_DEF;
>      rc = -1;
>    }
> +
> +  if (lgs_cfg_verify_log_record_destination_configuration(
> +      lgs_conf.logRecordDestinationConfiguration) == -1) {
> +    lgs_conf.logRecordDestinationConfiguration.clear();
> +    lgs_conf.logRecordDestinationConfiguration_cnfflag = LGS_CNF_DEF;
> +    rc = -1;
> +  }
>    TRACE_LEAVE();
> 
>    return rc;
> @@ -883,6 +989,18 @@ static void read_logsv_config_obj_2() {
>        lgs_conf.logFileSysConfig = *((SaUint32T *) value);
>        lgs_conf.logFileSysConfig_cnfflag = LGS_CNF_OBJ;
>        TRACE("Conf obj; logFileSysConfig: %u", lgs_conf.logFileSysConfig);
> +    } else if (!strcmp(attribute->attrName,
> +                       LOG_RECORD_DESTINATION_CONFIGURATION)) {
> +      // Note: Multi value
> +      char *value_string;
> +      for (uint32_t i = 0; i < attribute->attrValuesNumber; i++) {
> +        value = attribute->attrValues[i];
> +        value_string = *(reinterpret_cast<char **>(value));
> +
lgs_conf.logRecordDestinationConfiguration.push_back(value_string);
> +        TRACE("Conf obj; logRecordDestinationConfiguration: '%s'",
> +              lgs_conf.logRecordDestinationConfiguration.back().c_str());
> +      }
> +      lgs_conf.logRecordDestinationConfiguration_cnfflag = LGS_CNF_OBJ;
>      }
>    }
> 
> @@ -1125,10 +1243,6 @@ static void read_log_config_environ_var_
>   * Public functions for handling configuration information
> 
> **************************************************************
> ****************/
> 
> -/**
> - * Read the log service configuration data verify and update
configuration
> - * data structure
> - */
>  void lgs_cfg_init(SaImmOiHandleT immOiHandle, SaAmfHAStateT ha_state)
> {
>    TRACE_ENTER2("immOiHandle = %lld", immOiHandle);
> 
> @@ -1208,15 +1322,21 @@ const void *lgs_cfg_get(lgs_logconfGet_t
>      case LGS_IMM_LOG_MAX_APPLICATION_STREAMS:
>        value_ptr = &lgs_conf.logMaxApplicationStreams;
>        break;
> -    case LGS_IMM_FILEHDL_TIMEOUT:
> +    case LGS_IMM_FILE_IO_TIMEOUT:
>        value_ptr = &lgs_conf.logFileIoTimeout;
>        break;
> -    case LGS_IMM_LOG_FILESYS_CFG:
> +    case LGS_IMM_LOG_FILE_SYS_CONFIG:
>        value_ptr = &lgs_conf.logFileSysConfig;
>        break;
>      case LGS_IMM_LOG_OPENSAFLOGCONFIG_CLASS_EXIST:
>        value_ptr = &lgs_conf.OpenSafLogConfig_object_exist;
>        break;
> +    case LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION:
> +      value_ptr = &lgs_conf.logRecordDestinationConfiguration;
> +      break;
> +    case LGS_IMM_LOG_RECORD_DESTINATION_STATUS:
> +      value_ptr = &lgs_conf.logRecordDestinationStatus;
> +      break;
> 
>      case LGS_IMM_LOG_NUMBER_OF_PARAMS:
>      case LGS_IMM_LOG_NUMEND:
> @@ -1385,6 +1505,152 @@ void conf_runtime_obj_create(SaImmOiHand
>  }
> 
>  /**
> + * Same as immutil_update_one_rattr() except that
> + *
> + * All parameters are input parameters
> + *
> + */
> +SaAisErrorT update_multival_rattr(SaImmOiHandleT immOiHandle,
> +                                  const char *dn,
> +                                  SaImmAttrNameT attributeName,
> +                                  SaImmValueTypeT attrValueType,
> +                                     SaUint32T attrValuesNumber,
> +                                     void **values)
> +{
> +     SaImmAttrModificationT_2 attrMod;
> +     const SaImmAttrModificationT_2 *attrMods[] = { &attrMod, NULL };
> +     SaImmAttrValueT *attrValues = values;
> +     SaNameT objectName;
> +
> +     saAisNameLend(dn, &objectName);
> +
> +     attrMod.modType = SA_IMM_ATTR_VALUES_REPLACE;
> +     attrMod.modAttr.attrName = attributeName;
> +     attrMod.modAttr.attrValuesNumber = attrValuesNumber;
> +     attrMod.modAttr.attrValueType = attrValueType;
> +     attrMod.modAttr.attrValues = attrValues;
> +     return immutil_saImmOiRtObjectUpdate_2(immOiHandle,
> &objectName,
> +                                            attrMods);
> +}
> +
> +/**
> + * Creates a list of type void ** containing pointers to the
> + * strings in a C++ vector of strings
> + * Note: The attrValues C array is allocated and must be freed after use
> + *
> + * @param strings_in[in] C++ vector of strings
> + * @param attrValues[out] A C array with void pointers to C strings in
> + *                        strings_in
> + * @return attrValuesNumber
> + */
> +static SaUint32T vector_of_strings_to_attrValues(
> +          const std::vector<std::string>* strings_in, void
***attrValues_out) {
> +  TRACE_ENTER();
> +
> +  SaUint32T attrValuesNumber = strings_in->size();
> +
> +  char **values_array = (char **) calloc(attrValuesNumber, sizeof(void
**));
> +  if (values_array == nullptr) {
> +    LOG_ER("%s: calloc Fail, Aborted", __FUNCTION__);
> +    osaf_abort(0);
> +  }
> +  *attrValues_out = (void **) values_array;
> +
> +  SaUint32T i = 0;
> +  for (auto& conf_string : *strings_in) {
> +    values_array[i] = const_cast<char *>(conf_string.c_str());
> +    i++;
> +  }
> +
> +  TRACE_LEAVE();
> +  return attrValuesNumber;
> +}
> +
> +/**
> + * Update one runtime attribute of the specified object.
> + * Same as immutil_update_one_rattr() but updates a list of values (multi
> value)
> + *
> + * @param immOiHandle[in]
> + * @param dn[in]
> + * @param attributeName[in]
> + * @param attrValueType[in]
> + * @param valuesNumber[in]
> + * @param values[in]
> + * @return Return value from immutil_saImmOiRtObjectUpdate_2()
> + */
> +static SaAisErrorT update_runtime_attrValues(SaImmOiHandleT
> immOiHandle,
> +                                  const char *dn,
> +                                  SaImmAttrNameT attributeName,
> +                                  SaImmValueTypeT attrValueType,
> +                                     SaUint32T valuesNumber,
> +                                     void **values)
> +{
> +  TRACE_ENTER();
> +     SaImmAttrModificationT_2 attrMod;
> +     const SaImmAttrModificationT_2 *attrMods[] = { &attrMod, NULL };
> +     SaNameT objectName;
> +        SaAisErrorT ais_rc = SA_AIS_OK;
> +
> +    void **values_array = (void **) calloc(valuesNumber, sizeof(void *));
> +    if (values_array == nullptr) {
> +      LOG_ER("%s: calloc Fail, Aborted", __FUNCTION__);
> +      osaf_abort(0);
> +    }
> +
> +    for (SaUint32T i = 0; i < valuesNumber; i++) {
> +      values_array[i] = &values[i];
> +    }
> +
> +     saAisNameLend(dn, &objectName);
> +
> +     attrMod.modType = SA_IMM_ATTR_VALUES_REPLACE;
> +     attrMod.modAttr.attrName = attributeName;
> +     attrMod.modAttr.attrValuesNumber = valuesNumber;
> +     attrMod.modAttr.attrValueType = attrValueType;
> +     attrMod.modAttr.attrValues = values_array;
> +     ais_rc = immutil_saImmOiRtObjectUpdate_2(immOiHandle,
> &objectName,
> +                                                 attrMods);
> +        free(values_array);
> +        TRACE_LEAVE();
> +        return ais_rc;
> +}
> +
> +static SaAisErrorT update_lgs_cfg_runtime_multivalue(
> +                                              SaImmOiHandleT immOiHandle,
> +                                              SaImmAttrNameT
attributeName,
> +                                              SaImmValueTypeT valueType)
{
> +  TRACE_ENTER();
> +  // Get the multi value stored as C++ vector
> +  lgs_logconfGet_t attribute_id = param_name_to_id(attributeName);
> +  const std::vector<std::string> *multi_value_list =
> +      reinterpret_cast<const std::vector<std::string>*>
> +      (lgs_cfg_get(attribute_id));
> +
> +  // Convert the multi value C++ vector to a void C array
> +  void **attrValues = nullptr;
> +  SaUint32T attrValuesNumber = vector_of_strings_to_attrValues(
> +      multi_value_list, &attrValues);
> +
> +  // Give the multi value to IMM client
> +  SaAisErrorT ais_rc = update_runtime_attrValues(immOiHandle,
> +                                     LGS_CFG_RUNTIME_OBJECT,
> +                                     attributeName, valueType,
> +                                     attrValuesNumber, attrValues);
> +  if (ais_rc != SA_AIS_OK) {
> +    LOG_NO("%s: update_runtime_attrValues Fail %s",
> +           __FUNCTION__, saf_error(ais_rc));
> +  }
> +
> +  // Free the memory allocated by vector_of_strings_to_attrValues()
> +  if (attrValues) {
> +    free(attrValues);
> +  }
> +
> +  TRACE_LEAVE();
> +  return ais_rc;
> +}
> +
> +/**
>   * Handler for updating runtime configuration object attributes
>   * Called from the SaImmOiRtAttrUpdateCallbackT type function
>   *
> @@ -1401,7 +1667,6 @@ void conf_runtime_obj_hdl(SaImmOiHandleT
>    TRACE_ENTER();
> 
>    while ((attributeName = attributeNames[i++]) != NULL) {
> -    TRACE("Attribute %s", attributeName);
>      if (!strcmp(attributeName, LOG_ROOT_DIRECTORY)) {
>        str_val = (char *)
>            lgs_cfg_get(LGS_IMM_LOG_ROOT_DIRECTORY);
> @@ -1467,18 +1732,26 @@ void conf_runtime_obj_hdl(SaImmOiHandleT
>                                           &u32_val);
>      } else if (!strcmp(attributeName, LOG_FILE_IO_TIMEOUT)) {
>        u32_val = *(SaUint32T *)
> -          lgs_cfg_get(LGS_IMM_FILEHDL_TIMEOUT);
> +          lgs_cfg_get(LGS_IMM_FILE_IO_TIMEOUT);
>        ais_rc =  immutil_update_one_rattr(immOiHandle,
>                                           LGS_CFG_RUNTIME_OBJECT,
>                                           attributeName,
SA_IMM_ATTR_SAUINT32T,
>                                           &u32_val);
>      } else if (!strcmp(attributeName, LOG_FILE_SYS_CONFIG)) {
>        u32_val = *(SaUint32T *)
> -          lgs_cfg_get(LGS_IMM_LOG_FILESYS_CFG);
> +          lgs_cfg_get(LGS_IMM_LOG_FILE_SYS_CONFIG);
>        ais_rc =  immutil_update_one_rattr(immOiHandle,
>                                           LGS_CFG_RUNTIME_OBJECT,
>                                           attributeName,
SA_IMM_ATTR_SAUINT32T,
>                                           &u32_val);
> +    } else if (!strcmp(attributeName,
> LOG_RECORD_DESTINATION_CONFIGURATION)) {
> +      ais_rc = update_lgs_cfg_runtime_multivalue(immOiHandle,
> +                                                 attributeName,
> +                                                 SA_IMM_ATTR_SASTRINGT);
> +    } else if (!strcmp(attributeName, LOG_RECORD_DESTINATION_STATUS)) {
> +      ais_rc = update_lgs_cfg_runtime_multivalue(immOiHandle,
> +                                                 attributeName,
> +                                                 SA_IMM_ATTR_SASTRINGT);
>      } else {
>        TRACE("%s: unknown attribute %s",
>              __FUNCTION__, attributeName);
> @@ -1490,7 +1763,6 @@ void conf_runtime_obj_hdl(SaImmOiHandleT
>        osaf_abort(0);
>      }
>    }
> -
>    TRACE_LEAVE();
>  }
> 
> @@ -1552,6 +1824,22 @@ void lgs_trace_config() {
>    TRACE("logFileSysConfig\t\t %u,\t %s",
>          lgs_conf.logFileSysConfig,
>          cnfflag_str(lgs_conf.logFileSysConfig_cnfflag));
> +
> +  // Multivalue:
> +  for (auto& conf_str : lgs_conf.logRecordDestinationConfiguration) {
> +    TRACE("logRecordDestinationConfiguration '%s', %s", conf_str.c_str(),
> +
cnfflag_str(lgs_conf.logRecordDestinationConfiguration_cnfflag));
> +  }
> +  if (lgs_conf.logRecordDestinationConfiguration.empty()) {
> +    TRACE("logRecordDestinationConfiguration <empty>");
> +  }
> +  for (auto& conf_str : lgs_conf.logRecordDestinationStatus) {
> +    TRACE("logRecordDestinationStatus '%s'", conf_str.c_str());
> +  }
> +  if (lgs_conf.logRecordDestinationStatus.empty()) {
> +    TRACE("logRecordDestinationStatus <empty>");
> +  }
> +
>    TRACE("OpenSafLogConfig_object_exist\t %s",
>          lgs_conf.OpenSafLogConfig_object_exist ? "True": "False");
>    TRACE("===== LOG Configuration End =====");
> @@ -1584,8 +1872,21 @@ void lgs_cfg_read_trace() {
>    TRACE("logMaxApplicationStreams\t %u",
>          *static_cast<const SaUint32T
> *>(lgs_cfg_get(LGS_IMM_LOG_MAX_APPLICATION_STREAMS)));
>    TRACE("logFileIoTimeout\t\t %u",
> -        *static_cast<const SaUint32T
> *>(lgs_cfg_get(LGS_IMM_FILEHDL_TIMEOUT)));
> +        *static_cast<const SaUint32T
> *>(lgs_cfg_get(LGS_IMM_FILE_IO_TIMEOUT)));
>    TRACE("logFileSysConfig\t\t %u",
> -        *static_cast<const SaUint32T
> *>(lgs_cfg_get(LGS_IMM_LOG_FILESYS_CFG)));
> +        *static_cast<const SaUint32T
> *>(lgs_cfg_get(LGS_IMM_LOG_FILE_SYS_CONFIG)));
> +  // Multi value
> +  const std::vector<std::string> *dest_config =
> +      reinterpret_cast<const std::vector<std::string> *>
> +
> (lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION));
> +  for (auto& conf_str : *dest_config) {
> +    TRACE("logRecordDestinationConfiguration '%s'", conf_str.c_str());
> +  }
> +  const std::vector<std::string> *dest_status =
> +      reinterpret_cast<const std::vector<std::string> *>
> +      (lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_STATUS));
> +  for (auto& conf_str : *dest_status) {
> +    TRACE("logRecordDestinationStatus '%s'", conf_str.c_str());
> +  }
>    TRACE("##### LOG Configuration parameter read done  #####");
>  }
> diff --git a/src/log/logd/lgs_config.h b/src/log/logd/lgs_config.h
> --- a/src/log/logd/lgs_config.h
> +++ b/src/log/logd/lgs_config.h
> @@ -1,6 +1,7 @@
>  /*      -*- OpenSAF  -*-
>   *
>   * (C) Copyright 2015 The OpenSAF Foundation
> + * Copyright Ericsson AB [2015, 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
> @@ -15,12 +16,36 @@
>   *
>   */
> 
> +/***
> + * This handler contains handling of Log service configuration including:
> + *  - Configuration object
> + *  - Environment variables
> + *  - Default values
> + *  - Verification of attribute values
> + *  - Check-pointing
> + *  - Presenting of configuration related information using runtime
object
> + *
> + * All configuration data that can be found in the
> + * 'logConfig=1,safApp=safLogService' configuration object is stored
locally
> on
> + * both active and standby nodes. The nodes are synchronized using
> message based
> + * check-pointing. Local data is synchronized with changes in the
> configuration
> + * object in the OI.
> + *
> + * Note1:
> + * The primary interfaces are used for updating e.g. in OI, verifying
values
> + * e.g. in OI and getting values.
> + * There are also some help interfaces used for event handling of the
> + * OpenSafLogCurrentConfig information runtime object
> + *
> + */
> +
>  #ifndef LOG_LOGD_LGS_CONFIG_H_
>  #define LOG_LOGD_LGS_CONFIG_H_
> 
>  #include <stdbool.h>
>  #include <stdint.h>
>  #include <string>
> +#include <vector>
> 
>  #include "imm/saf/saImmOi.h"
>  #include "amf/saf/saAmf.h"
> @@ -38,6 +63,8 @@
>  #define LOG_MAX_APPLICATION_STREAMS "logMaxApplicationStreams"
>  #define LOG_FILE_IO_TIMEOUT "logFileIoTimeout"
>  #define LOG_FILE_SYS_CONFIG "logFileSysConfig"
> +#define LOG_RECORD_DESTINATION_CONFIGURATION
> "logRecordDestinationConfiguration"
> +#define LOG_RECORD_DESTINATION_STATUS
> "logRecordDestinationStatus"
> 
>  typedef enum {
>    LGS_IMM_LOG_ROOT_DIRECTORY,
> @@ -49,8 +76,10 @@ typedef enum {
>    LGS_IMM_LOG_STREAM_APP_HIGH_LIMIT,
>    LGS_IMM_LOG_STREAM_APP_LOW_LIMIT,
>    LGS_IMM_LOG_MAX_APPLICATION_STREAMS,
> -  LGS_IMM_FILEHDL_TIMEOUT,
> -  LGS_IMM_LOG_FILESYS_CFG,
> +  LGS_IMM_FILE_IO_TIMEOUT,
> +  LGS_IMM_LOG_FILE_SYS_CONFIG,
> +  LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION,
> +  LGS_IMM_LOG_RECORD_DESTINATION_STATUS,
> 
>    LGS_IMM_LOG_NUMBER_OF_PARAMS,
>    LGS_IMM_LOG_OPENSAFLOGCONFIG_CLASS_EXIST,
> @@ -58,6 +87,38 @@ typedef enum {
>    LGS_IMM_LOG_NUMEND
>  } lgs_logconfGet_t;
> 
> +static inline lgs_logconfGet_t param_name_to_id(std::string param_name)
> {
> +  if (param_name == LOG_ROOT_DIRECTORY) {
> +    return LGS_IMM_LOG_ROOT_DIRECTORY;
> +  } else if (param_name == LOG_DATA_GROUPNAME) {
> +    return LGS_IMM_DATA_GROUPNAME;
> +  } else if (param_name == LOG_MAX_LOGRECSIZE) {
> +    return LGS_IMM_LOG_MAX_LOGRECSIZE;
> +  } else if (param_name == LOG_STREAM_FILE_FORMAT) {
> +    return LGS_IMM_LOG_STREAM_FILE_FORMAT;
> +  } else if (param_name == LOG_STREAM_SYSTEM_HIGH_LIMIT) {
> +    return LGS_IMM_LOG_STREAM_SYSTEM_HIGH_LIMIT;
> +  } else if (param_name == LOG_STREAM_SYSTEM_LOW_LIMIT) {
> +    return LGS_IMM_LOG_STREAM_SYSTEM_LOW_LIMIT;
> +  } else if (param_name == LOG_STREAM_APP_HIGH_LIMIT) {
> +    return LGS_IMM_LOG_STREAM_APP_HIGH_LIMIT;
> +  } else if (param_name == LOG_STREAM_APP_LOW_LIMIT) {
> +    return LGS_IMM_LOG_STREAM_APP_LOW_LIMIT;
> +  } else if (param_name == LOG_MAX_APPLICATION_STREAMS) {
> +    return LGS_IMM_LOG_MAX_APPLICATION_STREAMS;
> +  } else if (param_name == LOG_FILE_IO_TIMEOUT) {
> +    return LGS_IMM_FILE_IO_TIMEOUT;
> +  } else if (param_name == LOG_FILE_SYS_CONFIG) {
> +    return LGS_IMM_LOG_FILE_SYS_CONFIG;
> +  } else if (param_name == LOG_RECORD_DESTINATION_CONFIGURATION)
> {
> +    return LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION;
> +  } else if (param_name == LOG_RECORD_DESTINATION_STATUS) {
> +    return LGS_IMM_LOG_RECORD_DESTINATION_STATUS;
> +  } else {
> +    return LGS_IMM_LOG_NUMEND; // Error
> +  }
> +}
> +
>  /**
>   * Structure for log server configuration changing data
>   */
> @@ -66,18 +127,184 @@ typedef struct config_chkpt {
>    uint64_t ckpt_buffer_size;      /* Total size of the buffer */
>  }lgs_config_chg_t;
> 
> -/*
> - * For function information see code file lgs_config.c
> +/* ----------------------------
> + * Primary interface functions
> + */
> +
> +/**
> + * Read the log service configuration data verify and update
configuration
> + * data structure
>   */
>  void lgs_cfg_init(SaImmOiHandleT immOiHandle, SaAmfHAStateT ha_state);
> +
> +/**
> + * Get value of log service configuration parameter from the
configuration
> data
> + * struct. The scope of configuration data is restricted to this file.
> + * This function gives read access to the configuration data
> + * Note: The type of the returned value is void. This means that the
reader
> + * must know the actual type and convert to correct type.
> + *
> + * Note1: Multivalue is returned as a void * to a std::vector<type>
> + * Example:
> + * LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION is returned as
> a void * to
> + * std::vector<std::string>
> + *
> + * @param lgs_logconfGet_t param[in]
> + *     Defines what configuration parameter to return
> + *
> + * @return void *
> + *     Pointer to the parameter. See struct lgs_conf
> + *
> + */
>  const void *lgs_cfg_get(lgs_logconfGet_t param);
> -bool lgs_path_is_writeable_dir_h(const std::string &pathname);
> +
> +/**
> + * Create configuration data buffer
> + * Creates a buffer containing all configuration data to be updated.
> + * Each time the function is called a configuration parameter is added.
> + *
> + * The buffer structure can be sent as a check-point message and the
> updating
> + * is done on the Standby. It can also be used in the OI to create an
update
> + * buffer in the apply callback. Use lgs_cfg_update() for updating the
> + * configuration.
> + *
> + * NOTE1: Parameter name and value is not validated
> + * NOTE2: This function allocates memory pointed to by config_data in the
> + *        lgs_config_chg_t structure. This memory has to bee freed after
usage
> + *        Example: free(config_data.ckpt_buffer_ptr);
> + * NOTE3: A multi-value is added by iterating the multivalue vector and
> enter
> + *        each value separately using the same attribute name (name_str).
> + *        Help functions for handling multi-value can be used.
> + *        See lgs_cfgupd_multival_xxx() functions.
> + *
> + * @param name_str[in]  String containing the parameter name
> + * @param value_str[in] Parmeter value as a string
> + * @param config_data[out] Filled in config data structure
> + *                         NOTE! Must be initiated before first call
{NULL, 0}
> + *
> + */
>  void lgs_cfgupd_list_create(const char *name_str, char *value_str,
>                              lgs_config_chg_t *config_data);
> +
> +/************************************************************
> *******************
> + * Help functions for handling multi value attributes with the cfgupd
list.
> + * Multi values can be handled in three ways Add, Delete and Replace
> + * In the configuration handler multi values will always be replaced.
These
> + * help functions will always create a complete list of values and add
them
> + * to the cfgupd list. Multiple values are added to the list by adding
> + * the same attribute multiple times with different values.
> + * When reading a list using lgs_cfgupd_list_read() a multi value will be
> given
> + * by returning the same attribute multiple times with different values
> + *
> + * Note1: The NO_DUPLICATES flag shall be used with multi value
attributes
> + *
> + * Note2: These help functions use lgs_cfgupd_list_create() so
information
> and
> + *        notes for lgs_cfgupd_list_create() regarding config_data also
applies
> + *        here.
> + *
> + * All functions takes the following parameters:
> + *
> + * @param attribute_name:
> + * The name of the multi value attribute
> + *
> + * @param value_list:
> + * A list of strings where each string represents a value
> + *
> + * @param config_data
> + * See lgs_cfgupd_list_create()
> + */
> +
> +/**
> + * Add a list of new values to the existing values in the attribute
> + *
> + */
> +void lgs_cfgupd_multival_add(const std::string& attribute_name,
> +                             const std::vector<std::string>& value_list,
> +                             lgs_config_chg_t *config_data);
> +
> +/**
> + * Delete the values given in the list from the multi value attribute
> + * Note: The attribute shall have the NO_DUPLICATES flag
> + *
> + */
> +void lgs_cfgupd_multival_delete(std::string attribute_name,
> +                                std::vector<std::string> value_list,
> +                                lgs_config_chg_t *config_data);
> +
> +/**
> + * Replace all existing values in the multi value attribute with the
values in
> + * the list
> + */
> +void lgs_cfgupd_mutival_replace(std::string attribute_name,
> +                                std::vector<std::string> value_list,
> +                                lgs_config_chg_t *config_data);
> +
> +/**
> + * Read a config update buffer and get parameter name and value.
> + * The first time the function is called next_param_ptr shall be
> + * a pointer to the buffer containing the config data.
> + * See ckpt_buffer_ptr in lgs_config_chg_t.
> + * To get the next parameter the next_param_ptr[in] shall be set to the
> + * return value from the previous call. NULL is returned when the last
> parameter
> + * is read.
> + * The last parameter cfgupd_ptr shall be a pointer to the buffer
structure
> to
> + * read
> + *
> + * NOTE1: The string pointed to by cfgupd_ptr->ckpt_buffer_ptr is changed
> in
> + *        this function! See strtok_r()
> + *
> + * NOTE2: See lgs_cfgupd_list_create() for info about multi-value
attribute
> + *
> + * @param name_str[out]
> + * @param value_str[out]
> + * @param next_param_ptr[in]
> + * @param cfgupd_ptr[in]
> + *
> + * @return next_param_ptr
> + */
>  char *lgs_cfgupd_list_read(char **name_str, char **value_str,
>                             char *next_param_ptr, lgs_config_chg_t
*cfgupd_ptr);
> +
> +/**
> + * Parse a configuration data buffer and update the configuration
structure.
> + * Used e.g. on Standby when receiving configuration check-point data and
> by OI
> + * to update configuration at modify apply.
> + *
> + * Comment: Check-pointed configuration data is always sent when the
> + *          configuration object is updated or when applying changes in
IO.
> + *          This means that the corresponding cnfflag is set to
LGS_CNF_OBJ
> + *
> + * NOTE1: Multi-value is always replaced. For information on how to
handle
> + *        Add, delete and replace see lgs_cfgupd_list_create() and the
> + *        multi-value handling help functions
> + *        This configuration is stored as a C++ vector of C++ strings
> + *        A completely new list is always created if this configuration
is
> + *        updated. This attribute may have multiple instances in the
> config_data
> + *        list. Each instance will add a value to the vector. If the
config_data
> + *        list has an empty string as value it means that the multivalue
is
> + *        empty and shall have an empty vector as value
> + *
> + * @param config_data[in] Pointer to structure containing configuration
> + *        data buffer
> + *
> + * @return -1 if validation error
> + */
>  int lgs_cfg_update(const lgs_config_chg_t *config_data);
> 
> +/**
> + * Parameter value validation functions. Validates parameters.
> + * For more information e.g. validation rules see lgs_conf.cc file
> + */
> +bool lgs_path_is_writeable_dir_h(const std::string &pathname);
> +int lgs_cfg_verify_root_dir(const std::string &root_str_in);
> +int lgs_cfg_verify_log_data_groupname(char *group_name);
> +int lgs_cfg_verify_log_file_format(const char* log_file_format);
> +int lgs_cfg_verify_max_logrecsize(uint32_t max_logrecsize_in);
> +int lgs_cfg_verify_mbox_limit(uint32_t high, uint32_t low);
> +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);
>  /*
>   * Functions for updating some parameters. Used to support check-point
> before
>   * version 5
> @@ -86,19 +313,8 @@ void lgs_rootpathconf_set(const std::str
>  void lgs_groupnameconf_set(const char *data_groupname_str);
> 
>  /*
> - * Parameter value validation functions. Validates parameters. See
function
> - * headers for validation rules
> - */
> -int lgs_cfg_verify_root_dir(const std::string &root_str_in);
> -int lgs_cfg_verify_log_data_groupname(char *group_name);
> -int lgs_cfg_verify_log_file_format(const char* log_file_format);
> -int lgs_cfg_verify_max_logrecsize(uint32_t max_logrecsize_in);
> -int lgs_cfg_verify_mbox_limit(uint32_t high, uint32_t low);
> -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);
> -
> -/*
> - * Handle runtime object for showing actual configuration
> + * Handle runtime object for showing actual configuration and
> configuration
> + * related information
>   */
>  void conf_runtime_obj_create(SaImmOiHandleT immOiHandle);
>  void conf_runtime_obj_hdl(SaImmOiHandleT immOiHandle, const
> SaImmAttrNameT *attributeNames);
> 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
> @@ -1225,7 +1225,7 @@ snd_rsp:
> 
> **************************************************************
> ***************/
>  static uint32_t proc_write_log_async_msg(lgs_cb_t *cb, lgsv_lgs_evt_t
> *evt) {
>    lgsv_write_log_async_req_t *param = &(evt-
> >info.msg.info.api_info.param).write_log_async;
> -  log_stream_t *stream;
> +  log_stream_t *stream = NULL;
>    SaAisErrorT error = SA_AIS_OK;
>    SaStringT logOutputString = NULL;
>    SaUint32T buf_size;
> diff --git a/src/log/logd/lgs_file.cc b/src/log/logd/lgs_file.cc
> --- a/src/log/logd/lgs_file.cc
> +++ b/src/log/logd/lgs_file.cc
> @@ -420,7 +420,7 @@ lgsf_retcode_t log_file_api(lgsf_apipar_
>    if (rc != 0) osaf_abort(rc);
> 
>    /* Wait for an answer */
> -  max_waittime_ms = *static_cast<const SaUint32T
> *>(lgs_cfg_get(LGS_IMM_FILEHDL_TIMEOUT));
> +  max_waittime_ms = *static_cast<const SaUint32T
> *>(lgs_cfg_get(LGS_IMM_FILE_IO_TIMEOUT));
>    get_timeout_time(&timeout_time, max_waittime_ms);
> 
>    while (lgs_com_data.answer_f == false) {
> 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
> @@ -1,6 +1,7 @@
>  /*      -*- OpenSAF  -*-
>   *
>   * (C) Copyright 2008 The OpenSAF Foundation
> + * Copyright Ericsson AB [2008, 2017] - All Rights Reserved
>   *
>   * This program is distributed in the hope that it will be useful, but
>   * WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY
> @@ -42,8 +43,8 @@
>  #include "log/logd/lgs.h"
>  #include "log/logd/lgs_util.h"
>  #include "log/logd/lgs_file.h"
> -#include "lgs_recov.h"
> -#include "lgs_config.h"
> +#include "log/logd/lgs_recov.h"
> +#include "log/logd/lgs_config.h"
>  #include "base/saf_error.h"
> 
>  #include "lgs_mbcsv_v1.h"
> @@ -740,10 +741,12 @@ static SaAisErrorT config_ccb_completed_
>      /**
>       *  Ignore deletion of attributes
>       *  except for logDataGroupname,
> -     *  and logStreamFileFormat
> +     *  logStreamFileFormat and
> +     *  logRecordDestinationConfiguration
>       */
>      if ((strcmp(attribute->attrName, LOG_DATA_GROUPNAME) != 0) &&
>          (strcmp(attribute->attrName, LOG_STREAM_FILE_FORMAT) != 0) &&
> +        (strcmp(attribute->attrName,
> LOG_RECORD_DESTINATION_CONFIGURATION) != 0) &&
>          (attribute->attrValuesNumber == 0)) {
>        report_oi_error(immOiHandle, opdata->ccbId,
>                        "deletion of value is not allowed for attribute %s
stream %s",
> @@ -846,6 +849,24 @@ static SaAisErrorT config_ccb_completed_
>                        "%s cannot be changed", attribute->attrName);
>        ais_rc = SA_AIS_ERR_FAILED_OPERATION;
>        goto done;
> +    } else if (!strcmp(attribute->attrName,
> +                       LOG_RECORD_DESTINATION_CONFIGURATION)) {
> +      // Note: Multi value attribute
> +      TRACE("logRecordDestinationConfiguration. Values number = %d",
> +            attribute->attrValuesNumber);
> +      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);
> +      if (rc == -1) {
> +        report_oi_error(immOiHandle, opdata->ccbId,
> +                        "%s value is NOT accepted", attribute->attrName);
> +        ais_rc = SA_AIS_ERR_INVALID_PARAM;
> +        goto done;
> +      }
>      } else {
>        report_oi_error(immOiHandle, opdata->ccbId,
>                        "attribute %s not recognized",
attribute->attrName);
> @@ -863,7 +884,7 @@ static SaAisErrorT config_ccb_completed_
>    }
> 
>  done:
> -  TRACE_LEAVE2("rc=%u", ais_rc);
> +  TRACE_LEAVE2("ais_rc='%s'", saf_error(ais_rc));
>    return ais_rc;
>  }
> 
> @@ -1972,8 +1993,7 @@ static void config_ccb_apply_modify(cons
>    /* Flag set if any of the mailbox limit values have changed */
>    bool mailbox_lim_upd = false;
> 
> -  TRACE_ENTER2("CCB ID %llu, '%s'", opdata->ccbId,
> -               osaf_extended_name_borrow(&opdata->objectName));
> +  TRACE_ENTER();
> 
>    attrMod = opdata->param.modify.attrMods[i++];
>    while (attrMod != NULL) {
> @@ -2058,6 +2078,39 @@ static void config_ccb_apply_modify(cons
>        lgs_cfgupd_list_create(LOG_FILE_IO_TIMEOUT,
>                               uint32_str, &config_data);
>      }
> +    else if (!strcmp(attribute->attrName,
> +                       LOG_RECORD_DESTINATION_CONFIGURATION)) {
> +      // Note: Multi value attribute
> +      TRACE("logRecordDestinationConfiguration");
> +      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);
> +      }
> +
> +      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);
> +      };
> +    }
> 
>      attrMod = opdata->param.modify.attrMods[i++];
>    }
> @@ -2085,10 +2138,12 @@ static void config_ccb_apply_modify(cons
>    /* Cleanup and free cfg buffer */
>    if (config_data.ckpt_buffer_ptr != NULL)
>      free(config_data.ckpt_buffer_ptr);
> +
> +  TRACE_LEAVE();
>  }
> 
>  static void config_ccb_apply(const CcbUtilOperationData_t *opdata) {
> -  TRACE_ENTER2("CCB ID %llu", opdata->ccbId);
> +  TRACE_ENTER();
> 
>    switch (opdata->operationType) {
>      case CCBUTIL_CREATE:
> 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
> @@ -367,7 +367,7 @@ bool lgs_is_peer_v5() {
>   */
>  bool lgs_is_split_file_system() {
>    SaUint32T lgs_file_config;
> -  lgs_file_config = *static_cast<const
> SaUint32T*>(lgs_cfg_get(LGS_IMM_LOG_FILESYS_CFG));
> +  lgs_file_config = *static_cast<const
> SaUint32T*>(lgs_cfg_get(LGS_IMM_LOG_FILE_SYS_CONFIG));
> 
>    if ((lgs_file_config == LGS_LOG_SPLIT_FILESYSTEM) && lgs_is_peer_v2())
{
>      return true;
> 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
> @@ -48,6 +48,7 @@ uint32_t ckpt_proc_lgs_cfg_v5(lgs_cb_t *
>    char *saved_buf = NULL;
>    int rc = 0;
> 
> +  TRACE_ENTER();
>    /* Flag set if any of the mailbox limit values have changed */
>    bool mailbox_lim_upd = false;
> 
> @@ -140,6 +141,7 @@ uint32_t ckpt_proc_lgs_cfg_v5(lgs_cb_t *
> 
>    lgs_trace_config();
> 
> +  TRACE_LEAVE();
>    return NCSCC_RC_SUCCESS;
>  }
> 


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

Reply via email to