Hi Lanert

ACK , ( code review only )  will be tested along with   Vu Part of #2258

-AVM

On 2/23/2017 3:41 PM, A V Mahesh wrote:
> Hi Lanert
>
> This means  , user can configure  multiple DESTINATION_CONFIGURATION
> concurrently  ?
>
>
> +      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);
> +      };
>
> -AVM
>
>
> On 2/20/2017 8:48 PM, Lennart Lund wrote:
>>    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) {
>> +    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) {
>> +    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) {
>> +    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) {
>> +  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) {
>> +  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


------------------------------------------------------------------------------
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