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

Reply via email to