Ack, Iam not yet through with the testing, but you please go ahead to push the patch as you deem appropriate.
Thanks, Mathi. ----- vu.m.ngu...@dektech.com.au wrote: > Sorry. Please take this attach. > > Regards, Vu. > > > >-----Original Message----- > >From: Vu Minh Nguyen [mailto:vu.m.ngu...@dektech.com.au] > >Sent: Thursday, February 25, 2016 9:23 AM > >To: 'Mathivanan Naickan Palanivelu'; lennart.l...@ericsson.com > >Cc: opensaf-devel@lists.sourceforge.net > >Subject: Re: [devel] [PATCH 1 of 4] log: add support for cloud > resilience feature > >(service part) [#1179] > > > >Thanks, Mathi. > > > >Here is the fix for comments of patch 1/4 and 2/4. > > > >Regards, Vu. > > > >>-----Original Message----- > >>From: Mathivanan Naickan Palanivelu > [mailto:mathi.naic...@oracle.com] > >>Sent: Wednesday, February 24, 2016 5:24 PM > >>To: vu.m.ngu...@dektech.com.au > >>Cc: lennart.l...@ericsson.com; opensaf-devel@lists.sourceforge.net; > >>anders.wid...@ericsson.com > >>Subject: Re: [PATCH 1 of 4] log: add support for cloud resilience > feature > >(service > >>part) [#1179] > >> > >>Ack for patch 1, > >>You could use %PRIx64 for MDS addresses. > >>Mathi. > >> > >>----- vu.m.ngu...@dektech.com.au wrote: > >> > >>> osaf/services/saf/logsv/lgs/Makefile.am | 6 +- > >>> osaf/services/saf/logsv/lgs/lgs.h | 12 + > >>> osaf/services/saf/logsv/lgs/lgs_cb.h | 14 + > >>> osaf/services/saf/logsv/lgs/lgs_evt.cc | 66 ++- > >>> osaf/services/saf/logsv/lgs/lgs_evt.h | 5 + > >>> osaf/services/saf/logsv/lgs/lgs_file.cc | 5 + > >>> osaf/services/saf/logsv/lgs/lgs_file.h | 1 + > >>> osaf/services/saf/logsv/lgs/lgs_filehdl.cc | 424 > ++++++++++++++++- > >>> osaf/services/saf/logsv/lgs/lgs_filehdl.h | 21 + > >>> osaf/services/saf/logsv/lgs/lgs_imm.cc | 541 > >>> ++++++++++++++++++++- > >>> osaf/services/saf/logsv/lgs/lgs_main.cc | 134 ++++- > >>> osaf/services/saf/logsv/lgs/lgs_mbcsv.cc | 22 +- > >>> osaf/services/saf/logsv/lgs/lgs_recov.cc | 758 > >>> +++++++++++++++++++++++++++++ > >>> osaf/services/saf/logsv/lgs/lgs_recov.h | 37 + > >>> osaf/services/saf/logsv/lgs/lgs_stream.cc | 65 +- > >>> osaf/services/saf/logsv/lgs/lgs_stream.h | 28 +- > >>> osaf/services/saf/logsv/lgs/lgs_util.cc | 50 +- > >>> osaf/services/saf/logsv/lgs/lgs_util.h | 4 + > >>> 18 files changed, 2121 insertions(+), 72 deletions(-) > >>> > >>> > >>> The patch makes LOG service be able to handle the case that both > SC > >>> nodes > >>> are down at the same time. > >>> When one or both nodes go up again the log service must be able > to > >>> resume its work preferably without actions by the clients. > >>> > >>> A log client should not have to be aware of if one or both SC > nodes > >>> are down. > >>> The only thing that should happen is that a TRY AGAIN (and in > some > >>> cases TIMEOUT) returned. > >>> It is the responsibility of the client to decide how to handle > this. > >>> > >>> diff --git a/osaf/services/saf/logsv/lgs/Makefile.am > >>> b/osaf/services/saf/logsv/lgs/Makefile.am > >>> --- a/osaf/services/saf/logsv/lgs/Makefile.am > >>> +++ b/osaf/services/saf/logsv/lgs/Makefile.am > >>> @@ -35,7 +35,8 @@ noinst_HEADERS = \ > >>> lgs_mbcsv_v1.h \ > >>> lgs_mbcsv_v2.h \ > >>> lgs_mbcsv_v3.h \ > >>> - lgs_mbcsv_v5.h > >>> + lgs_mbcsv_v5.h \ > >>> + lgs_recov.h > >>> > >>> osaf_execbindir = $(pkglibdir) > >>> osaf_execbin_PROGRAMS = osaflogd > >>> @@ -63,7 +64,8 @@ osaflogd_SOURCES = \ > >>> lgs_mbcsv_v1.cc \ > >>> lgs_mbcsv_v2.cc \ > >>> lgs_mbcsv_v3.cc \ > >>> - lgs_mbcsv_v5.cc > >>> + lgs_mbcsv_v5.cc \ > >>> + lgs_recov.cc > >>> > >>> osaflogd_LDADD = \ > >>> $(top_builddir)/osaf/tools/safimm/src/libimmutil.la \ > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs.h > >>> b/osaf/services/saf/logsv/lgs/lgs.h > >>> --- a/osaf/services/saf/logsv/lgs/lgs.h > >>> +++ b/osaf/services/saf/logsv/lgs/lgs.h > >>> @@ -30,6 +30,8 @@ > >>> #include <ncs_edu_pub.h> > >>> #include <ncs_util.h> > >>> #include <saAis.h> > >>> +#include <saf_error.h> > >>> +#include <saImmOm.h> > >>> > >>> /* LGS files */ > >>> #include "lgsv_defs.h" > >>> @@ -111,6 +113,16 @@ extern void lgs_imm_impl_reinit_nonblock > >>> extern void lgs_imm_init_OI_handle(SaImmOiHandleT *immOiHandle, > >>> SaSelectionObjectT > >>> *immSelectionObject); > >>> extern void lgs_imm_impl_set(SaImmOiHandleT immOiHandle); > >>> +extern SaAisErrorT lgs_imm_init_configStreams(lgs_cb_t *cb); > >>> > >>> +// Functions for recovery handling > >>> +void lgs_clean_stream_objects(void); > >>> +void lgs_delete_one_stream_object(char *name_str); > >>> +void lgs_search_stream_objects(void); > >>> +SaUint32T *lgs_get_scAbsenceAllowed_attr(SaUint32T *attr_val); > >>> +int lgs_get_streamobj_attr(SaImmAttrValuesT_2 ***attrib_out, > >>> + char *object_name, > >>> + SaImmHandleT *immOmHandle); > >>> +int lgs_free_streamobj_attr(SaImmHandleT immHandle); > >>> > >>> #endif /* ifndef __LGS_H */ > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_cb.h > >>> b/osaf/services/saf/logsv/lgs/lgs_cb.h > >>> --- a/osaf/services/saf/logsv/lgs/lgs_cb.h > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_cb.h > >>> @@ -26,6 +26,14 @@ > >>> > >>> #include "lgs_stream.h" > >>> > >>> +/* LGS Recovery states */ > >>> +typedef enum { > >>> + LGS_NORMAL, /* No recovery is ongoing. All requests are > >>> handled normally */ > >>> + LGS_RECOVERY /* Recover streams if in recovery list when > >>> stream open > >>> + * request with no parameters > >>> + */ > >>> +} lgs_state_t; > >>> + > >>> /* Default HA state assigned locally during lgs initialization > */ > >>> #define LGS_HA_INIT_STATE 0 > >>> > >>> @@ -87,6 +95,12 @@ typedef struct lgs_cb { > >>> LGA_DOWN_LIST *lga_down_list_tail; > >>> > >>> bool nid_started; /**< true if started by NID */ > >>> + SaUint32T scAbsenceAllowed; /* OpenSAF global configuration for > >>> recovery handling */ > >>> + lgs_state_t lgs_recovery_state; /* Indicate current recovery > state > >>> for the server */ > >>> + > >>> + // Initialize default value in contructor > >>> + lgs_cb() : lgs_recovery_state(LGS_NORMAL) {}; > >>> + > >>> } lgs_cb_t; > >>> > >>> extern uint32_t lgs_cb_init(lgs_cb_t *); > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_evt.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_evt.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_evt.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_evt.cc > >>> @@ -21,6 +21,7 @@ > >>> > >>> #include "lgs_mbcsv_v1.h" > >>> #include "lgs_mbcsv_v2.h" > >>> +#include "lgs_recov.h" > >>> > >>> /* Macro to validate the version */ > >>> #define m_LOG_VER_IS_VALID(ver) \ > >>> @@ -375,6 +376,8 @@ static uint32_t proc_lga_updn_mds_msg(lg > >>> break; > >>> > >>> case LGSV_LGS_EVT_LGA_DOWN: > >>> + TRACE("%s: LGSV_LGS_EVT_LGA_DOWN mds_dest = %" PRIu64, > >>> + __FUNCTION__, evt->fr_dest); > >>> if ((lgs_cb->ha_state == SA_AMF_HA_ACTIVE) || (lgs_cb- > >>>ha_state == > >>> SA_AMF_HA_QUIESCED)) { > >>> /* Remove this LGA entry from our processing lists */ > >>> osaf_clock_gettime(CLOCK_REALTIME, > >>&closetime_tspec); > >>> @@ -772,11 +775,17 @@ static uint32_t lgs_ckpt_stream_open(lgs > >>> > >>> /** > >>> * Create a new application stream > >>> - * @param open_sync_param > >>> - * @param o_stream > >>> - * @return > >>> + * > >>> + * @param open_sync_param[in] Parameters used to create the > stream > >>> + * @param o_stream[out] The created stream > >>> + * @param create_object_f IMM stream object is created > >>> + * > >>> + * @return AIS return code > >>> */ > >>> -static SaAisErrorT create_new_app_stream(lgsv_stream_open_req_t > >>> *open_sync_param, log_stream_t **o_stream) > >>> +SaAisErrorT create_new_app_stream( > >>> + lgsv_stream_open_req_t *open_sync_param, > >>> + log_stream_t **o_stream, > >>> + int creationFlag) > >>> { > >>> SaAisErrorT rc = SA_AIS_OK; > >>> log_stream_t *stream; > >>> @@ -870,7 +879,7 @@ static SaAisErrorT create_new_app_stream > >>> goto done; > >>> } > >>> > >>> - stream = log_stream_new(&open_sync_param->lstr_name, > >>> + stream = log_stream_new_1(&open_sync_param->lstr_name, > >>> open_sync_param->logFileName, > >>> open_sync_param->logFilePathName, > >>> open_sync_param->maxLogFileSize, > >>> @@ -878,7 +887,11 @@ static SaAisErrorT create_new_app_stream > >>> open_sync_param->logFileFullAction, > >>> open_sync_param->maxFilesRotated, > >>> open_sync_param->logFileFmt, > >>> - STREAM_TYPE_APPLICATION, STREAM_NEW, > >>twelveHourModeFlag, 0); > >>> + STREAM_TYPE_APPLICATION, > >>> + STREAM_NEW, > >>> + twelveHourModeFlag, > >>> + 0, > >>> + creationFlag); > >>> > >>> if (stream == NULL) { > >>> rc = SA_AIS_ERR_NO_MEMORY; > >>> @@ -960,6 +973,7 @@ static uint32_t proc_stream_open_msg(lgs > >>> log_stream_t *logStream; > >>> char name[SA_MAX_NAME_LENGTH + 1]; > >>> time_t file_closetime = 0; > >>> + int i_rc = 0; > >>> > >>> /* Create null-terminated stream name */ > >>> memcpy(name, open_sync_param->lstr_name.value, > >>> open_sync_param->lstr_name.length); > >>> @@ -981,10 +995,12 @@ static uint32_t proc_stream_open_msg(lgs > >>> /* One of the well-known log streams */ > >>> if (open_sync_param->lstr_open_flags & > >>SA_LOG_STREAM_CREATE) { > >>> ais_rv = SA_AIS_ERR_INVALID_PARAM; > >>> + rc = NCSCC_RC_FAILURE; > >>> goto snd_rsp; > >>> } > >>> } > >>> } else { > >>> + /* Stream does not exist */ > >>> if (cb->immOiHandle == 0) { > >>> TRACE("IMM service unavailable, open stream failed"); > >>> ais_rv = SA_AIS_ERR_TRY_AGAIN; > >>> @@ -992,20 +1008,48 @@ static uint32_t proc_stream_open_msg(lgs > >>> } > >>> > >>> if ((open_sync_param->lstr_open_flags & > >>SA_LOG_STREAM_CREATE) == 0) > >>> { > >>> - ais_rv = SA_AIS_ERR_NOT_EXIST; > >>> - goto snd_rsp; > >>> + /* The stream does not exist but the create flag is not > >>> + * set. If lgs_state is LGS_RECOVERY then: > >>> + * Check if the stream is in the list of stream objects > >>> + * not recovered. If in list, recover the stream and > >>> + * add it to the client > >>> + */ > >>> + if (lgs_cb->lgs_recovery_state == LGS_RECOVERY) { > >>> + TRACE("%s LGS_RECOVERY",__FUNCTION__); > >>> + i_rc = lgs_restore_one_app_stream(name, > >>> + open_sync_param->client_id, > >>> + &logStream); > >>> + if (i_rc == -1) { > >>> + TRACE("%s lgs_restore_one_stream > >>Fail", __FUNCTION__); > >>> + ais_rv = SA_AIS_ERR_NOT_EXIST; > >>> + goto snd_rsp; > >>> + } > >>> + TRACE("%s Stream %s is recovered", > >>__FUNCTION__, name); > >>> + log_stream_print(logStream); /* TRACE */ > >>> + lstr_id = logStream->streamId; > >>> + goto snd_rsp; > >>> + } else { > >>> + /* Trying to open a non existing stream */ > >>> + TRACE("%s Attempt to open not existing > >>stream", __FUNCTION__); > >>> + ais_rv = SA_AIS_ERR_NOT_EXIST; > >>> + goto snd_rsp; > >>> + } > >>> } > >>> > >>> /* Create the stream: > >>> * - Check parameters > >>> * - Create the stream in the stream "data base" > >>> * - If active create IMM runtime object > >>> + * - If recover do not create IMM object > >>> * > >>> * Note: Files are not created here > >>> */ > >>> - ais_rv = create_new_app_stream(open_sync_param, > >>&logStream); > >>> - if (ais_rv != SA_AIS_OK) > >>> + ais_rv = create_new_app_stream(open_sync_param, > >>&logStream, 1); > >>> + if (ais_rv != SA_AIS_OK) { > >>> + TRACE("%s create_new_app_stream Fail \"%s\"", > >>> + __FUNCTION__, saf_error(ais_rv)); > >>> goto snd_rsp; > >>> + } > >>> } > >>> > >>> /* Create the log files: > >>> @@ -1030,7 +1074,7 @@ static uint32_t proc_stream_open_msg(lgs > >>> goto snd_rsp; > >>> } > >>> > >>> - TRACE_4("logStream->streamId = %u", logStream->streamId); > >>> + TRACE_4("logStream->streamId = %u is created", > >>> logStream->streamId); > >>> lstr_id = logStream->streamId; > >>> > >>> snd_rsp: > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_evt.h > >>> b/osaf/services/saf/logsv/lgs/lgs_evt.h > >>> --- a/osaf/services/saf/logsv/lgs/lgs_evt.h > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_evt.h > >>> @@ -77,4 +77,9 @@ extern uint32_t lgs_remove_lga_down_rec( > >>> extern void lgs_send_write_log_ack(uint32_t client_id, > SaInvocationT > >>> invocation, SaAisErrorT error, MDS_DEST mds_dest); > >>> extern void lgs_free_write_log(const lgsv_write_log_async_req_t > >>> *param); > >>> > >>> +SaAisErrorT create_new_app_stream( > >>> + lgsv_stream_open_req_t *open_sync_param, > >>> + log_stream_t **o_stream, > >>> + int creationFlag); > >>> + > >>> #endif /*!LGS_EVT_H */ > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_file.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_file.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_file.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_file.cc > >>> @@ -184,6 +184,11 @@ static void *file_hndl_thread(void *nopa > >>> hndl_rc = > >>own_log_files_by_group_hdl(lgs_com_data.indata_ptr, > >>> lgs_com_data.outdata_ptr, > >>lgs_com_data.outdata_size); > >>> break; > >>> + case LGSF_GET_FILE_PAR: > >>> + hndl_rc = > >>lgs_get_file_params_hdl(lgs_com_data.indata_ptr, > >>> + > >>lgs_com_data.outdata_ptr, > >>> + > >>lgs_com_data.outdata_size); > >>> + break; > >>> default: > >>> break; > >>> } > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_file.h > >>> b/osaf/services/saf/logsv/lgs/lgs_file.h > >>> --- a/osaf/services/saf/logsv/lgs/lgs_file.h > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_file.h > >>> @@ -43,6 +43,7 @@ typedef enum { > >>> LGSF_CHECKPATH, > >>> LGSF_CHECKDIR, > >>> LGSF_OWN_LOGFILES, > >>> + LGSF_GET_FILE_PAR, > >>> LGSF_NOREQ > >>> }lgsf_treq_t; > >>> > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_filehdl.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_filehdl.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_filehdl.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_filehdl.cc > >>> @@ -23,7 +23,6 @@ > >>> #include <dirent.h> > >>> #include <fcntl.h> > >>> #include <sys/stat.h> > >>> - > >>> #include <unistd.h> > >>> > >>> #include <logtrace.h> > >>> @@ -731,3 +730,426 @@ done_exit: > >>> TRACE_LEAVE(); > >>> return rc; > >>> } > >>> + > >>> +/* > >>> > >>***************************************************************** > >* > >>********** > >>> + * lgs_get_file_params_hdl() with help functions > >>> + * > >>> + * Find the file that was current log file before server down. > >>> + * Get file size > >>> + * Get last written record Id by reading the first characters of > the > >>> last > >>> + * log record in the file > >>> + * > >>> + */ > >>> + > >>> +/** > >>> + * Count the occurrences of character <c> in a string > >>> + * Count from end of string and stop when lim <c> is found or > >>> + * beginning of string > >>> + * > >>> + * @param str[in] '\0' terminated string to count characters in > >>> + * @param c[in] Character to count > >>> + * @param lim[in] Stop count when lim characters found > >>> + * @return number of occurrences > >>> + */ > >>> +static int chr_cnt_b(char *str, char c, int lim) > >>> +{ > >>> + int cnt = 0; > >>> + > >>> + if ((str == NULL) || (*str == '\0')) { > >>> + TRACE("%s: Parameter error", __FUNCTION__); > >>> + return 0; > >>> + } > >>> + > >>> + char *ptr_str_end = strchr(str, '\0'); > >>> + char *ptr_str_pos = ptr_str_end - 1; > >>> + > >>> + while (1) { > >>> + if (*ptr_str_pos == c) > >>> + cnt++; > >>> + > >>> + /* End if beginning of file or char limit */ > >>> + if ((ptr_str_pos == str) || (cnt >= lim)) > >>> + break; > >>> + > >>> + ptr_str_pos--; > >>> + } > >>> + > >>> + return cnt; > >>> +} > >>> + > >>> +/** > >>> + * Filter function used by scandir. > >>> + * Find a current log file if it exist > >>> + * - name as in file_name_find[] > >>> + * - extension .log > >>> + * - two timestamps (other log files has four) > >>> + */ > >>> +/* Filename prefix (no timestamps or extension */ > >>> +static std::string file_name_find_g; > >>> +static int filter_logfile_name(const struct dirent *finfo) > >>> +{ > >>> + bool name_found = false, ext_found = false; > >>> + > >>> + if (strstr(finfo->d_name, file_name_find_g.c_str()) != NULL) > >>> + name_found = true; > >>> + if (strstr(finfo->d_name, ".log") != NULL) > >>> + ext_found = true; > >>> + > >>> + return (int) (name_found && ext_found); > >>> +} > >>> + > >>> +/** > >>> + * Get name and size of log file containing the last record Id. > Is > >>> current log > >>> + * file if not empty or last rotated if current log file is > empty > >>> + * Also give name of current log file if exist > >>> + * > >>> + * @param filepath_i[in] Path to log file directory > >>> + * @param filename_i[in] Name prefix for file to search for > >>> + * @param filename_o[out] Name of file with Id. "" if no file > >>> found > >>> + * @param curname_o[out] Name of current log file or "" if > no > >>> current found > >>> + * @param fsize[out] Size of current log file > >>> + * @return -1 on error filename_o is not valid > >>> + */ > >>> +static int filename_get( > >>> + char *filepath_i, char *filename_i, > >>> + std::string &filename_o, std::string &curname_o, > >>> + uint32_t *fsize) > >>> +{ > >>> + int rc = 0; > >>> + int num_files = 0; > >>> + int i = 0; > >>> + struct dirent **namelist; > >>> + struct stat statbuf; > >>> + std::string file_path; > >>> + double time_tmp; > >>> + char *str_p = NULL; > >>> + int len = 0; > >>> + /* Time newest file */ > >>> + double time_new = 0; > >>> + /* Index in namelist for newest and second newest file */ > >>> + int name_ix_prv = 0, name_ix_new = 0; > >>> + /* Set to true if file is empty */ > >>> + bool empty_flg_prv = true, empty_flg_new = true; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + // /* Initiate out data */ > >>> + filename_o.clear(); > >>> + curname_o.clear(); > >>> + *fsize = 0; > >>> + > >>> + /* Create a list of all .log files that has > >>> + * <filename_i> in <filepath_i> > >>> + */ > >>> + file_name_find_g = filename_i; > >>> + num_files = scandir(filepath_i, &namelist, filter_logfile_name, > >>> alphasort); > >>> + if (num_files == -1) { > >>> + TRACE("%s: scandir Fail %s", __FUNCTION__, strerror(errno)); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + if (num_files == 0) { > >>> + /* There is no log file at all */ > >>> + TRACE("\t No log file at all found"); > >>> + goto done_free; > >>> + } > >>> + > >>> + /* Special case, only one file is found > >>> + */ > >>> + if (num_files == 1) { > >>> + file_path = std::string(filepath_i) + "/" + > namelist[0]->d_name; > >>> + if (stat(file_path.c_str(), &statbuf) == -1) { > >>> + TRACE("%s: stat() Fail %s", __FUNCTION__, > >>strerror(errno)); > >>> + rc = -1; > >>> + goto done_free; > >>> + } > >>> + > >>> + /* Save found file name */ > >>> + filename_o = namelist[0]->d_name; > >>> + > >>> + /* Handle current log file output */ > >>> + goto done_hdl_cur; > >>> + } > >>> + > >>> + /* Find the newest and the second newest file in the list > >>> + * Return in filename_o name of newest file if not empty else > >>> + * the second newest (if that also is empty return error) > >>> + */ > >>> + time_new = time_tmp = 0; > >>> + name_ix_prv = name_ix_new = 0; > >>> + empty_flg_prv = empty_flg_new = false; > >>> + > >>> + for (i = 0; i < num_files; i++) { > >>> + file_path = std::string(filepath_i) + "/" + > namelist[i]->d_name; > >>> + if (stat(file_path.c_str(), &statbuf) == -1) { > >>> + TRACE("%s: stat() Fail %s", __FUNCTION__, > >>strerror(errno)); > >>> + rc = -1; > >>> + goto done_free; > >>> + } > >>> + > >>> + time_tmp = osaf_timespec_to_double(&statbuf.st_mtim); > >>> + if (time_tmp > time_new) { > >>> + name_ix_prv = name_ix_new; > >>> + name_ix_new = i; > >>> + time_new = time_tmp; > >>> + empty_flg_prv = empty_flg_new; > >>> + if (statbuf.st_size == 0) > >>> + empty_flg_new = true; > >>> + else > >>> + empty_flg_new = false; > >>> + } > >>> + } > >>> + > >>> + /* Give found filename for output */ > >>> + *fsize = 0; > >>> + if (empty_flg_new == false) { > >>> + /* Give the newest filename and its size. File is not empty */ > >>> + filename_o = namelist[name_ix_new]->d_name; > >>> + *fsize = statbuf.st_size; > >>> + } else if (empty_flg_prv == false) { > >>> + /* Give the second newest filename. File is not empty */ > >>> + filename_o = namelist[name_ix_prv]->d_name; > >>> + } else { > >>> + /* Both files are empty. This is an error */ > >>> + TRACE("%s: Both newest and second newest are empy", > >>__FUNCTION__); > >>> + filename_o.clear(); > >>> + rc = -1; > >>> + } > >>> + > >>> +done_hdl_cur: > >>> + /* Handle current log file output */ > >>> + len = strlen(filename_i); > >>> + str_p = namelist[name_ix_new]->d_name + len; > >>> + if (chr_cnt_b(str_p, '_', 4) == 2) { > >>> + /* Newest is current log file */ > >>> + curname_o = namelist[name_ix_new]->d_name; > >>> + } > >>> + > >>> +done_free: > >>> + /* Free namelist */ > >>> + for (i = 0; i < num_files; i++) { > >>> + free(namelist[i]); > >>> + } > >>> + > >>> + free(namelist); > >>> + > >>> +done: > >>> + TRACE_LEAVE(); > >>> + return rc; > >>> +} > >>> + > >>> +/** > >>> + * Get last record Id from the given file. > >>> + * Each record begins with a record Id number. The wanted Id is > the > >>> last Id > >>> + * in the file > >>> + * > >>> + * @param file_name[in] Complete path to the file > >>> + * @return record Id or -1 on error. Set to 0 if no error but no > Id > >>> is found > >>> + */ > >>> +static int record_id_get(const char *file_path) > >>> +{ > >>> + FILE *fd = NULL; > >>> + int id_rc = 0; > >>> + int i; > >>> + int c; > >>> + long endpos; > >>> + > >>> + char *read_line = NULL; > >>> + size_t dummy_n = 0; > >>> + ssize_t line_len; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + /* Open the file */ > >>> + while (1) { > >>> + fd = fopen(file_path, "r"); > >>> + if (fd != NULL) { > >>> + break; > >>> + } else if (errno != EINTR){ > >>> + TRACE("%s fopen Fail %s", > >>> + __FUNCTION__, strerror(errno)); > >>> + id_rc = -1; > >>> + goto done; > >>> + } > >>> + } > >>> + > >>> + /* Get last pos in file */ > >>> + while(1) { > >>> + if (fseek(fd, 0, SEEK_END) == -1) { > >>> + if (errno == EINTR) > >>> + continue; > >>> + TRACE("\t %d fseek Fail %s",__LINE__, strerror(errno)); > >>> + id_rc = -1; > >>> + goto done; > >>> + } > >>> + break; > >>> + } > >>> + endpos = ftell(fd); > >>> + > >>> + if (endpos == 0) { > >>> + /* The file is empty. No Id to find */ > >>> + id_rc = 0; > >>> + goto done; > >>> + } > >>> + > >>> + /* Search from the end for '\n' (end of prev record) > >>> + * or beginning of file > >>> + */ > >>> + for (i = 2; i < endpos; i++) { > >>> + while(1) { > >>> + if (fseek(fd, -i, SEEK_END) == -1) { > >>> + if (errno == EINTR) > >>> + continue; > >>> + TRACE("\t %d fseek Fail %s", > >>> + __LINE__, strerror(errno)); > >>> + id_rc = -1; > >>> + goto done; > >>> + } > >>> + break; > >>> + } > >>> + > >>> + c = getc(fd); > >>> + if (c == '\n') > >>> + break; > >>> + } > >>> + > >>> + /* Get pos for line start */ > >>> + c = getc(fd); /* Dummy read '\n' */ > >>> + > >>> + /* Read the last record and get record id */ > >>> + line_len = getline(&read_line, &dummy_n, fd); > >>> + if (line_len == -1) { > >>> + TRACE("%s: getline Fail %s", > >>> + __FUNCTION__, strerror(errno)); > >>> + id_rc = -1; > >>> + goto done; > >>> + } > >>> + id_rc = atoi(read_line); > >>> + if (id_rc == 0) { > >>> + TRACE("%s: \"%s\" has no number. Id set to 0", > >>> + __FUNCTION__, read_line); > >>> + id_rc = 0; > >>> + goto done_free; > >>> + } > >>> + > >>> +done_free: > >>> + free(read_line); > >>> + > >>> +done: > >>> + if (fd != NULL) > >>> + fclose(fd); > >>> + > >>> + TRACE_LEAVE(); > >>> + return id_rc; > >>> +} > >>> + > >>> +/** > >>> + * Get log file information: > >>> + * - Name of current log file: <name prefix>_YYMMDD_HHMMSS > >>> + * - Log record id > >>> + * > >>> + * Rules: > >>> + * - If current log file not empty: Name = cur log file, Size = > file > >>> size, > >>> + * Id = fr file, rc = OK > >>> + * - If current log file empty no rotated: Name = cur log file, > Size > >>> = 0, > >>> + * Id = 1, rc = OK > >>> + * - If current log file empty is rotated: Name = cur log file, > Size > >>> = 0, > >>> + * Id = fr last rotated, > rc = > >>> OK > >>> + * - If no log file at all: Name = NULL, Size = 0, Id = 1, rc = > OK > >>> + * - If only rotated log file: Name = NULL, Size = 0, Id = fr > rotated > >>> file, > >>> + * rc = OK > >>> + * > >>> + * @param indata[in] gfp_in_t > >>> + * file_name: File name prefix (name part before time > stamps) > >>> + * file_path: Full path to directory root path + rel path > >>> + * > >>> + * @param outdata[out] gfp_out_t > >>> + * curFileName: Current file name <name > prefix>_YYMMDD_HHMMSS > >>> + * curFileSize: Bytes written to current log file (file > size) > >>> + * logRecordId: log record identifier for last written > log > >>> record > >>> + > >>> + * @param max_outsize[in] sizeof gfp_out_t (not used) > >>> + * > >>> + * @return (-1) on error, out data not valid > >>> + */ > >>> +int lgs_get_file_params_hdl(void *indata, void *outdata, size_t > >>> max_outsize) > >>> +{ > >>> + gfp_in_t *par_in = static_cast<gfp_in_t *>(indata); > >>> + gfp_out_t *par_out = static_cast<gfp_out_t *>(outdata); > >>> + int rc = 0, int_rc = 0; > >>> + std::string file_name; > >>> + std::string file_name_cur; > >>> + std::string file_path; > >>> + int rec_id = 0; > >>> + char *ptr_str; > >>> + > >>> + uint32_t file_size = 0; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + osaf_mutex_unlock_ordie(&lgs_ftcom_mutex); /* UNLOCK Critical > >>> section */ > >>> + > >>> + /* Initialize out parameters */ > >>> + par_out->curFileName = NULL; > >>> + par_out->curFileSize = 0; > >>> + par_out->logRecordId = 0; > >>> + > >>> + /* Get log file to get info from and its size */ > >>> + int_rc = filename_get( > >>> + par_in->file_path, par_in->file_name, > >>> + file_name, file_name_cur, &file_size > >>> + ); > >>> + if (int_rc == -1) { > >>> + TRACE("%s: filename_get Fail",__FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + if (file_name[0] == '\0') { > >>> + TRACE("No file found."); > >>> + rc = -1; > >>> + par_out->logRecordId = 0; > >>> + goto done; > >>> + } > >>> + > >>> + /* Create the file path */ > >>> + file_path = std::string(par_in->file_path) + "/" + file_name; > >>> + > >>> + /* Find record id in file */ > >>> + rec_id = record_id_get(file_path.c_str()); > >>> + if (rec_id == -1) { > >>> + TRACE("%s: record_id_get Fail", __FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + /* Fill in out data */ > >>> + par_out->logRecordId = rec_id; > >>> + par_out->curFileSize = file_size; > >>> + if (file_name_cur.empty() == false) { > >>> + // This memory will be deleted in > >>log_stream_open_file_restore() > >>> + // who wants to get the info from this memory - > >>> par_out->curFileName > >>> + par_out->curFileName = static_cast<char *>( > >>> + calloc(1, file_name_cur.size() + 1)); > >>> + if (par_out->curFileName == NULL) { > >>> + LOG_ER("%s Failed to allocate memory", > >>__FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + strcpy(par_out->curFileName, file_name_cur.c_str()); > >>> + /* Remove extension */ > >>> + ptr_str = strstr(par_out->curFileName, ".log"); > >>> + if (ptr_str == NULL) { > >>> + TRACE("%s: Could not find .log extension Fail", > >>__FUNCTION__); > >>> + rc = -1; > >>> + } > >>> + *ptr_str = '\0'; > >>> + } > >>> + > >>> +done: > >>> + osaf_mutex_lock_ordie(&lgs_ftcom_mutex); /* LOCK after critical > >>> section */ > >>> + TRACE_LEAVE2("rc = %d", rc); > >>> + return rc; > >>> +} > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_filehdl.h > >>> b/osaf/services/saf/logsv/lgs/lgs_filehdl.h > >>> --- a/osaf/services/saf/logsv/lgs/lgs_filehdl.h > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_filehdl.h > >>> @@ -155,6 +155,26 @@ typedef struct { > >>> * No out parameters > >>> */ > >>> > >>> +/* > >>> + * lgs_get_file_params_hdl(...) > >>> + * Ret code -1 if error > >>> + */ > >>> +/* IN params */ > >>> +typedef struct { > >>> + /* File name prefix (name part before time stamps) */ > >>> + char *file_name; > >>> + /* Full path to directory root path + rel path */ > >>> + char *file_path; > >>> +} gfp_in_t; > >>> + > >>> +/* OUT params */ > >>> +typedef struct { > >>> + char *curFileName; > >>> + uint32_t curFileSize; /* Bytes written to current log file */ > >>> + uint32_t logRecordId; /* log record identifier increased for > each > >>> record */ > >>> +} gfp_out_t; > >>> + > >>> + > >>> int path_is_writeable_dir_hdl(void *indata, void *outdata, > size_t > >>> max_outsize); > >>> int check_path_exists_hdl(void *indata, void *outdata, size_t > >>> max_outsize); > >>> int rename_file_hdl(void *indata, void *outdata, size_t > >>> max_outsize); > >>> @@ -166,6 +186,7 @@ int fileclose_hdl(void *indata, void *ou > >>> int delete_file_hdl(void *indata, void *outdata, size_t > >>> max_outsize); > >>> int get_number_of_log_files_hdl(void *indata, void *outdata, > size_t > >>> max_outsize); > >>> int own_log_files_by_group_hdl(void *indata, void *outdata, > size_t > >>> max_outsize); > >>> +int lgs_get_file_params_hdl(void *indata, void *outdata, size_t > >>> max_outsize); > >>> > >>> #endif /* LGS_FILEHDL_H */ > >>> > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_imm.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_imm.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_imm.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_imm.cc > >>> @@ -42,6 +42,7 @@ > >>> #include "lgs.h" > >>> #include "lgs_util.h" > >>> #include "lgs_file.h" > >>> +#include "lgs_recov.h" > >>> #include "lgs_config.h" > >>> #include "saf_error.h" > >>> > >>> @@ -2507,7 +2508,8 @@ done: > >>> * @return SaAisErrorT > >>> */ > >>> static SaAisErrorT stream_create_and_configure(const char *dn, > >>> - log_stream_t **in_stream, int stream_id, > >>SaImmAccessorHandleT > >>> accessorHandle) > >>> + log_stream_t **in_stream, int stream_id, > >>> + SaImmAccessorHandleT accessorHandle) > >>> { > >>> SaAisErrorT rc = SA_AIS_OK; > >>> SaNameT objectName; > >>> @@ -2606,6 +2608,15 @@ static SaAisErrorT stream_create_and_con > >>> } else if (!strcmp(attribute->attrName, > >>> "saLogStreamSeverityFilter")) { > >>> stream->severityFilter = *((SaUint32T *)value); > >>> TRACE("severityFilter: %u", stream->severityFilter); > >>> + } else if (!strcmp(attribute->attrName, > >>> "saLogStreamCreationTimestamp")) { > >>> + if (attribute->attrValuesNumber != 0) { > >>> + /* Restore creation timestamp if exist > >>> + * Note: Creation timestamp in stream is set to > >>> + * current time which will be the value if no > >>> + * value found in object > >>> + */ > >>> + stream->creationTimeStamp = > >>*(static_cast<SaTimeT *>(value)); > >>> + } > >>> } > >>> } > >>> > >>> @@ -2648,11 +2659,15 @@ static const SaImmOiCallbacksT_2 callbac > >>> * IMM-OM interface and initialize the corresponding information > >>> * in the LOG control block. Initialize the LOG IMM-OI > >>> * interface. Become class implementer. > >>> + * > >>> + * @param immOiHandle[in] > >>> + * @return > >>> */ > >>> -SaAisErrorT lgs_imm_create_configStream(lgs_cb_t *cb) > >>> +SaAisErrorT lgs_imm_init_configStreams(lgs_cb_t *cb) > >>> { > >>> - SaAisErrorT rc = SA_AIS_OK; > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> SaAisErrorT om_rc; > >>> + int int_rc = 0; > >>> log_stream_t *stream; > >>> SaImmHandleT omHandle; > >>> SaImmAccessorHandleT accessorHandle; > >>> @@ -2685,9 +2700,13 @@ SaAisErrorT lgs_imm_create_configStream( > >>> &immSearchHandle)) == SA_AIS_OK) { > >>> > >>> while (immutil_saImmOmSearchNext_2(immSearchHandle, > >>&objectName, > >>> &attributes) == SA_AIS_OK) { > >>> - if ((rc = stream_create_and_configure((char*) > >>objectName.value, > >>> - &stream, streamId, accessorHandle)) != > >>SA_AIS_OK) { > >>> - LOG_ER("stream_create_and_configure failed > >>%d", rc); > >>> + /* Note: Here is creationTimeStamp and severityFilter > >>set > >>> + * Can be recovered if scAbseceAllowed > >>> + */ > >>> + ais_rc = stream_create_and_configure((char*) > >>objectName.value, > >>> + &stream, streamId, accessorHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + LOG_WA("stream_create_and_configure failed > >>%d", ais_rc); > >>> goto done; > >>> } > >>> streamId += 1; > >>> @@ -2709,12 +2728,35 @@ SaAisErrorT lgs_imm_create_configStream( > >>> > >>> stream = log_stream_getnext_by_name(NULL); > >>> while (stream != NULL) { > >>> - (void)immutil_update_one_rattr(cb->immOiHandle, stream- > >>>name, > >>> - > >>> const_cast<SaImmAttrNameT>("saLogStreamCreationTimestamp"), > >>> - SA_IMM_ATTR_SATIMET, > >>> - &stream->creationTimeStamp); > >>> - > >>> - log_stream_open_fileinit(stream); > >>> + if (cb->scAbsenceAllowed != 0) { > >>> + /** > >>> + * This code to make LOG distinguish b/w headless and > >>cluster > >>> start-up/restarts > >>> + * 1) If data reading from IMM differs with default > >>ones, headless > >>> just occurs > >>> + * 2) If data reading from IMM equals to default ones, > >>continue > >>> checking: > >>> + * 2.1) If info in cfg file are equal to data reading > >>> from > >>IMM, > >>> can reuse the cfg/log files > >>> + * 2.2) If info in cfg are not equal to, can not re-use > >>cfg/log > >>> file. Creating new ones instead. > >>> + */ > >>> + int_rc = log_stream_open_file_restore(stream); > >>> + if (int_rc == -1) { > >>> + /* Restore failed. Initialize instead */ > >>> + LOG_NO("%s: log_stream_open_file_restore > >>Fail", __FUNCTION__); > >>> + log_stream_open_fileinit(stream); > >>> + stream->creationTimeStamp = > >>lgs_get_SaTime(); > >>> + } > >>> + } else { > >>> + /* Always create new files and set current timestamp > >>to > >>> + * current time > >>> + */ > >>> + log_stream_open_fileinit(stream); > >>> + stream->creationTimeStamp = lgs_get_SaTime(); > >>> + } > >>> + > >>> + (void)immutil_update_one_rattr( > >>> + cb->immOiHandle, stream->name, > >>> + > >> const_cast<SaImmAttrNameT>("saLogStreamCreationTimestamp"), > >>> + SA_IMM_ATTR_SATIMET, > >>> + &stream->creationTimeStamp); > >>> + > >>> stream = log_stream_getnext_by_name(stream->name); > >>> } > >>> > >>> @@ -2740,7 +2782,7 @@ SaAisErrorT lgs_imm_create_configStream( > >>> immutilWrapperProfile.errorsAreFatal = errorsAreFatal; /* > Enable > >>> again */ > >>> > >>> TRACE_LEAVE(); > >>> - return rc; > >>> + return ais_rc; > >>> } > >>> > >>> /** > >>> @@ -2951,3 +2993,476 @@ void lgs_imm_impl_reinit_nonblocking(lgs > >>> > >>> TRACE_LEAVE(); > >>> } > >>> + > >>> > >>+/*************************************************************** > >* > >>************** > >>> + * Functions used for recovery handling > >>> + > >>> > >>***************************************************************** > >* > >>************/ > >>> +/** > >>> + * Search for old runtime stream objects and put their names in > a > >>> list. > >>> + * Become OI for the runtime stream class if any objects found > >>> + * > >>> + * @param object_list > >>> + */ > >>> +void lgs_search_stream_objects(void) > >>> +{ > >>> + int rc = 0; > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> + SaImmHandleT immOmHandle; > >>> + SaImmSearchHandleT immSearchHandle; > >>> + const char* class_name = "SaLogStream"; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + /* Save immutil settings and reconfigure immutil > >>> + */ > >>> + struct ImmutilWrapperProfile tmp_immutilWrapperProfile; > >>> + tmp_immutilWrapperProfile.errorsAreFatal = > >>> immutilWrapperProfile.errorsAreFatal; > >>> + tmp_immutilWrapperProfile.nTries = > immutilWrapperProfile.nTries; > >>> + tmp_immutilWrapperProfile.retryInterval = > >>> immutilWrapperProfile.retryInterval; > >>> + > >>> + immutilWrapperProfile.errorsAreFatal = 0; > >>> + immutilWrapperProfile.nTries = 500; > >>> + immutilWrapperProfile.retryInterval = 1000; > >>> + > >>> + > >>> + /* Intialize Om API > >>> + */ > >>> + ais_rc = immutil_saImmOmInitialize(&immOmHandle, NULL, > >>> &immVersion); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + LOG_WA("%s saImmOmInitialize FAIL %d", __FUNCTION__, > >>ais_rc); > >>> + goto done; > >>> + } > >>> + > >>> + /* Initialize search for application stream runtime objects > >>> + * Search for all objects of class 'SaLogStream' > >>> + * Search criteria: > >>> + * Attribute SaImmAttrClassName == SaLogStream > >>> + */ > >>> + SaImmSearchParametersT_2 searchParam; > >>> + searchParam.searchOneAttr.attrName = const_cast<char > >>> *>("SaImmAttrClassName"); > >>> + searchParam.searchOneAttr.attrValueType = > >>SA_IMM_ATTR_SASTRINGT; > >>> + searchParam.searchOneAttr.attrValue = &class_name; > >>> + SaNameT root_name; > >>> + root_name.value[0] = '\0'; > >>> + root_name.length = 1; > >>> + > >>> + ais_rc = immutil_saImmOmSearchInitialize_2( > >>> + immOmHandle, > >>> + &root_name, > >>> + SA_IMM_SUBTREE, > >>> + SA_IMM_SEARCH_ONE_ATTR | > >>SA_IMM_SEARCH_GET_NO_ATTR, > >>> + &searchParam, > >>> + NULL, > >>> + &immSearchHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + LOG_WA("%s saImmOmSearchInitialize FAIL %d", > >>__FUNCTION__, > >>> ais_rc); > >>> + goto done_fin_Om; > >>> + } > >>> + > >>> + /* Iterate the search and save objects in list until all > objects > >>> found > >>> + */ > >>> + SaNameT object_name; > >>> + SaImmAttrValuesT_2 **attributes; > >>> + > >>> + ais_rc = immutil_saImmOmSearchNext_2(immSearchHandle, > >>&object_name, > >>> &attributes); > >>> + if (ais_rc == SA_AIS_ERR_NOT_EXIST) { > >>> + TRACE("\tNo objects found"); > >>> + } > >>> + > >>> + while (ais_rc == SA_AIS_OK) { > >>> + TRACE("\tFound object \"%s\"", reinterpret_cast<char > >>> *>(object_name.value)); > >>> + /* Add the string to the list > >>> + */ > >>> + rc = log_rtobj_list_add(reinterpret_cast<char > >>> *>(object_name.value)); > >>> + if (rc == -1) { > >>> + TRACE("%s Could not add %s to list Fail", > >>> + __FUNCTION__, reinterpret_cast<char > >>*>(object_name.value)); > >>> + } > >>> + > >>> + /* Get next object */ > >>> + ais_rc = immutil_saImmOmSearchNext_2(immSearchHandle, > >>> + &object_name, &attributes); > >>> + } > >>> + if (ais_rc == SA_AIS_ERR_NOT_EXIST) { > >>> + TRACE("\tAll objects found OK"); > >>> + } else { > >>> + LOG_WA("\t%s saImmOmSearchNext FAILED %d", > >>__FUNCTION__, ais_rc); > >>> + } > >>> + > >>> + /* Finalize the serach API > >>> + */ > >>> + ais_rc = immutil_saImmOmSearchFinalize(immSearchHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("\tsaImmOmSearchFinalize FAIL %d", ais_rc); > >>> + } > >>> + > >>> +done_fin_Om: > >>> + /* Finalize the Om API > >>> + */ > >>> + ais_rc = immutil_saImmOmFinalize(immOmHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("\tsaImmOmFinalize FAIL %d", ais_rc); > >>> + } > >>> + > >>> +done: > >>> + /* Restore immutil settings */ > >>> + immutilWrapperProfile.errorsAreFatal = > >>> tmp_immutilWrapperProfile.errorsAreFatal; > >>> + immutilWrapperProfile.nTries = > tmp_immutilWrapperProfile.nTries; > >>> + immutilWrapperProfile.retryInterval = > >>> tmp_immutilWrapperProfile.retryInterval; > >>> + > >>> + TRACE_LEAVE(); > >>> +} > >>> + > >>> +void lgs_delete_one_stream_object(char *name_str) > >>> +{ > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> + SaNameT object_name; > >>> + > >>> + if (name_str == NULL) { > >>> + TRACE("%s No object name given", __FUNCTION__); > >>> + return; > >>> + } > >>> + > >>> + /* Save immutil settings and reconfigure */ > >>> + struct ImmutilWrapperProfile tmp_immutilWrapperProfile; > >>> + tmp_immutilWrapperProfile.errorsAreFatal = > >>> immutilWrapperProfile.errorsAreFatal; > >>> + tmp_immutilWrapperProfile.nTries = > immutilWrapperProfile.nTries; > >>> + tmp_immutilWrapperProfile.retryInterval = > >>> immutilWrapperProfile.retryInterval; > >>> + > >>> + immutilWrapperProfile.errorsAreFatal = 0; > >>> + immutilWrapperProfile.nTries = 500; > >>> + immutilWrapperProfile.retryInterval = 1000; > >>> + > >>> + /* Copy name to a SaNameT */ > >>> + (void) strncpy(reinterpret_cast<char *>(object_name.value), > >>> + name_str, SA_MAX_NAME_LENGTH); > >>> + object_name.length = strlen(name_str) + 1; > >>> + > >>> + /* and delete the object */ > >>> + ais_rc = immutil_saImmOiRtObjectDelete(lgs_cb->immOiHandle, > >>> &object_name); > >>> + if (ais_rc == SA_AIS_OK) { > >>> + TRACE("%s Object \"%s\" deleted", __FUNCTION__, > >>> + reinterpret_cast<char *>(object_name.value)); > >>> + } else { > >>> + LOG_WA("%s saImmOiRtObjectDelete for \"%s\" FAILED %d", > >>> + __FUNCTION__, reinterpret_cast<char > >>*>(object_name.value), > >>> ais_rc); > >>> + } > >>> + > >>> + /* Restore immutil settings */ > >>> + immutilWrapperProfile.errorsAreFatal = > >>> tmp_immutilWrapperProfile.errorsAreFatal; > >>> + immutilWrapperProfile.nTries = > tmp_immutilWrapperProfile.nTries; > >>> + immutilWrapperProfile.retryInterval = > >>> tmp_immutilWrapperProfile.retryInterval; > >>> +} > >>> + > >>> +/** > >>> + * Delete all stream objects in the stream object list. > >>> + * See also: lgs_search_stream_objects() > >>> + * > >>> + */ > >>> +void lgs_clean_stream_objects(void) > >>> +{ > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> + int pos = 0; > >>> + char *name_str; > >>> + SaNameT object_name; > >>> + > >>> + TRACE_ENTER(); > >>> + /* Check if there are any objects to delete. If not do nothing > */ > >>> + if (log_rtobj_list_no() <= 0) { > >>> + TRACE("%s\t No objects to delete is found", __FUNCTION__); > >>> + return; > >>> + } > >>> + > >>> + /* Save immutil settings and reconfigure */ > >>> + struct ImmutilWrapperProfile tmp_immutilWrapperProfile; > >>> + tmp_immutilWrapperProfile.errorsAreFatal = > >>> immutilWrapperProfile.errorsAreFatal; > >>> + tmp_immutilWrapperProfile.nTries = > immutilWrapperProfile.nTries; > >>> + tmp_immutilWrapperProfile.retryInterval = > >>> immutilWrapperProfile.retryInterval; > >>> + > >>> + immutilWrapperProfile.errorsAreFatal = 0; > >>> + immutilWrapperProfile.nTries = 500; > >>> + immutilWrapperProfile.retryInterval = 1000; > >>> + > >>> + pos = log_rtobj_list_getnamepos(); > >>> + while (pos != -1) { > >>> + /* Get found name */ > >>> + name_str = log_rtobj_list_getname(pos); > >>> + > >>> + /* Delete the object if in object list. Note this is an Oi > >>> operation > >>> + * Remove name from list > >>> + */ > >>> + if (name_str != NULL) { > >>> + /* Copy name to a SaNameT */ > >>> + (void) strncpy(reinterpret_cast<char > >>*>(object_name.value), > >>> + name_str, SA_MAX_NAME_LENGTH); > >>> + object_name.length = strlen(name_str) + 1; > >>> + /* and delete the object */ > >>> + ais_rc = immutil_saImmOiRtObjectDelete(lgs_cb- > >>>immOiHandle, > >>> + &object_name); > >>> + if (ais_rc == SA_AIS_OK) { > >>> + TRACE("\tObject \"%s\" deleted", > >>> + reinterpret_cast<char > >>*>(object_name.value)); > >>> + } else { > >>> + LOG_WA("%s saImmOiRtObjectDelete for > >>\"%s\" FAILED %d", > >>> + __FUNCTION__, > >>> + reinterpret_cast<char > >>*>(object_name.value), ais_rc); > >>> + } > >>> + } else { > >>> + /* Should never happen! */ > >>> + TRACE("%s\tFound name has NULL pointer!", > >>__FUNCTION__); > >>> + } > >>> + /* Remove deleted object name from list */ > >>> + log_rtobj_list_erase_one_pos(pos); > >>> + > >>> + /* Get next object */ > >>> + pos = log_rtobj_list_getnamepos(); > >>> + } > >>> + > >>> + /* Restore immutil settings */ > >>> + immutilWrapperProfile.errorsAreFatal = > >>> tmp_immutilWrapperProfile.errorsAreFatal; > >>> + immutilWrapperProfile.nTries = > tmp_immutilWrapperProfile.nTries; > >>> + immutilWrapperProfile.retryInterval = > >>> tmp_immutilWrapperProfile.retryInterval; > >>> + > >>> + TRACE_LEAVE(); > >>> +} > >>> + > >>> +/** > >>> + * Get cached stream attributes for given object name > >>> + * > >>> + * @param attrib_out[out] Pointer to a NULL terminated > >>> SaImmAttrValuesT_2 vector > >>> + * @param object_name[in] String containing the object name > >>> + * @param immOmHandle[out] > >>> + * @return -1 on error > >>> + */ > >>> +int lgs_get_streamobj_attr(SaImmAttrValuesT_2 ***attrib_out, > char > >>> *object_name_in, > >>> + SaImmHandleT *immOmHandle) > >>> +{ > >>> + int rc = 0; > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> + SaImmAccessorHandleT accessorHandle; > >>> + char *attribute_names[] = { > >>> + const_cast<char *>("saLogStreamFileName"), > >>> + const_cast<char *>("saLogStreamPathName"), > >>> + const_cast<char *>("saLogStreamMaxLogFileSize"), > >>> + const_cast<char *>("saLogStreamFixedLogRecordSize"), > >>> + const_cast<char *>("saLogStreamHaProperty"), > >>> + const_cast<char *>("saLogStreamLogFullAction"), > >>> + const_cast<char *>("saLogStreamMaxFilesRotated"), > >>> + const_cast<char *>("saLogStreamLogFileFormat"), > >>> + const_cast<char *>("saLogStreamSeverityFilter"), > >>> + const_cast<char *>("saLogStreamCreationTimestamp"), > >>> + NULL > >>> + }; > >>> + > >>> + TRACE_ENTER2("object_name_in \"%s\"", object_name_in); > >>> + > >>> + SaNameT object_name; > >>> + > >>> + /* Save immutil settings and reconfigure */ > >>> + struct ImmutilWrapperProfile tmp_immutilWrapperProfile; > >>> + tmp_immutilWrapperProfile.errorsAreFatal = > >>> immutilWrapperProfile.errorsAreFatal; > >>> + tmp_immutilWrapperProfile.nTries = > immutilWrapperProfile.nTries; > >>> + tmp_immutilWrapperProfile.retryInterval = > >>> immutilWrapperProfile.retryInterval; > >>> + > >>> + immutilWrapperProfile.errorsAreFatal = 0; > >>> + immutilWrapperProfile.nTries = 500; > >>> + immutilWrapperProfile.retryInterval = 1000; > >>> + > >>> + if (object_name_in == NULL) { > >>> + TRACE("%s No object name given (NULL)", __FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + /* Initialize Om API > >>> + */ > >>> + ais_rc = immutil_saImmOmInitialize(immOmHandle, NULL, > >>&immVersion); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + LOG_WA("\t%s saImmOmInitialize FAIL %d", __FUNCTION__, > >>ais_rc); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + /* Initialize accessor for reading attributes > >>> + */ > >>> + ais_rc = immutil_saImmOmAccessorInitialize(*immOmHandle, > >>> &accessorHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("%s\t saImmOmAccessorInitialize Fail '%s'", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + strncpy(reinterpret_cast<char *>(object_name.value), > >>> + object_name_in, SA_MAX_NAME_LENGTH); > >>> + object_name.length = strlen(reinterpret_cast<char > >>> *>(object_name.value)) + 1; > >>> + > >>> + ais_rc = immutil_saImmOmAccessorGet_2(accessorHandle, > >>&object_name, > >>> + attribute_names, attrib_out); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("%s\t saImmOmAccessorGet_2 Fail '%s'", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + rc = -1; > >>> + goto done_fin_Om; > >>> + } else { > >>> + goto done; > >>> + } > >>> + > >>> +done_fin_Om: > >>> + /* Free resources if fail */ > >>> + ais_rc = immutil_saImmOmFinalize(*immOmHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("%s\t saImmOmFinalize Fail '%s'", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + } > >>> + > >>> +done: > >>> + /* Restore immutil settings */ > >>> + immutilWrapperProfile.errorsAreFatal = > >>> tmp_immutilWrapperProfile.errorsAreFatal; > >>> + immutilWrapperProfile.nTries = > tmp_immutilWrapperProfile.nTries; > >>> + immutilWrapperProfile.retryInterval = > >>> tmp_immutilWrapperProfile.retryInterval; > >>> + > >>> + TRACE_LEAVE(); > >>> + return rc; > >>> +} > >>> + > >>> +/** > >>> + * Free resources claimed when calling lgs_get_streamobj_attr() > >>> + * > >>> + * @param immHandle[in] > >>> + * @return -1 on error > >>> + */ > >>> +int lgs_free_streamobj_attr(SaImmHandleT immOmHandle) > >>> +{ > >>> + int rc = 0; > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + /* Save immutil settings and reconfigure */ > >>> + struct ImmutilWrapperProfile tmp_immutilWrapperProfile; > >>> + tmp_immutilWrapperProfile.errorsAreFatal = > >>> immutilWrapperProfile.errorsAreFatal; > >>> + tmp_immutilWrapperProfile.nTries = > immutilWrapperProfile.nTries; > >>> + tmp_immutilWrapperProfile.retryInterval = > >>> immutilWrapperProfile.retryInterval; > >>> + > >>> + immutilWrapperProfile.errorsAreFatal = 0; > >>> + immutilWrapperProfile.nTries = 500; > >>> + immutilWrapperProfile.retryInterval = 1000; > >>> + > >>> + ais_rc = immutil_saImmOmFinalize(immOmHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("%s\t saImmOmFinalize Fail '%s'", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + rc = -1; > >>> + } > >>> + > >>> + /* Restore immutil settings */ > >>> + immutilWrapperProfile.errorsAreFatal = > >>> tmp_immutilWrapperProfile.errorsAreFatal; > >>> + immutilWrapperProfile.nTries = > tmp_immutilWrapperProfile.nTries; > >>> + immutilWrapperProfile.retryInterval = > >>> tmp_immutilWrapperProfile.retryInterval; > >>> + > >>> + TRACE_LEAVE(); > >>> + return rc; > >>> +} > >>> + > >>> +/** > >>> + * Read the scAbsenceAllowed attribute in > >>> + * opensafImm=opensafImm,safApp=safImmService object. > >>> + * No value means that "absence handling" is not allowed. For > Log > >>> this means > >>> + * that no recovery shall be done. > >>> + * > >>> + * @param attr_val[out] Value read from scAbsenceAllowed > attribute. 0 > >>> = No value > >>> + * @return Pointer to attr_val if value is read. NULL if no > value. If > >>> NULL the > >>> + * value in attr_val is not valid > >>> + */ > >>> +SaUint32T *lgs_get_scAbsenceAllowed_attr(SaUint32T *attr_val) > >>> +{ > >>> + SaUint32T *rc_attr_val = NULL; > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> + SaImmAccessorHandleT accessorHandle; > >>> + SaImmHandleT immOmHandle; > >>> + SaImmAttrValuesT_2 *attribute; > >>> + SaImmAttrValuesT_2 **attributes; > >>> + > >>> + TRACE_ENTER(); > >>> + char *attribute_names[] = { > >>> + const_cast<char *>("scAbsenceAllowed"), > >>> + NULL > >>> + }; > >>> + char object_name_str[] = > >>> "opensafImm=opensafImm,safApp=safImmService"; > >>> + > >>> + SaNameT object_name; > >>> + strncpy(reinterpret_cast<char *>(object_name.value), > >>> + object_name_str, SA_MAX_NAME_LENGTH); > >>> + object_name.length = strlen(reinterpret_cast<char > >>> *>(object_name.value)) + 1; > >>> + > >>> + /* Default restore handling shall be disabled. Is enabled if > the > >>> + * scAbsenceAllowed attribute is not empty > >>> + */ > >>> + *attr_val = 0; > >>> + > >>> + /* Save immutil settings and reconfigure */ > >>> + struct ImmutilWrapperProfile tmp_immutilWrapperProfile; > >>> + tmp_immutilWrapperProfile.errorsAreFatal = > >>> immutilWrapperProfile.errorsAreFatal; > >>> + tmp_immutilWrapperProfile.nTries = > immutilWrapperProfile.nTries; > >>> + tmp_immutilWrapperProfile.retryInterval = > >>> immutilWrapperProfile.retryInterval; > >>> + > >>> + immutilWrapperProfile.errorsAreFatal = 0; > >>> + immutilWrapperProfile.nTries = 500; > >>> + immutilWrapperProfile.retryInterval = 1000; > >>> + > >>> + /* Initialize Om API > >>> + */ > >>> + ais_rc = immutil_saImmOmInitialize(&immOmHandle, NULL, > >>> &immVersion); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + LOG_WA("\t%s saImmOmInitialize FAIL %d", __FUNCTION__, > >>ais_rc); > >>> + goto done; > >>> + } > >>> + > >>> + /* Initialize accessor for reading attributes > >>> + */ > >>> + ais_rc = immutil_saImmOmAccessorInitialize(immOmHandle, > >>> &accessorHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("%s\t saImmOmAccessorInitialize Fail '%s'", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + goto done; > >>> + } > >>> + > >>> + > >>> + ais_rc = immutil_saImmOmAccessorGet_2(accessorHandle, > >>&object_name, > >>> + attribute_names, &attributes); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("%s\t saImmOmAccessorGet_2 Fail '%s'", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + goto done_fin_Om; > >>> + } > >>> + > >>> + void *value; > >>> + > >>> + /* Handle the global scAbsenceAllowed_flag */ > >>> + attribute = attributes[0]; > >>> + TRACE("%s\t attrName \"%s\"", __FUNCTION__, > attribute->attrName); > >>> + if ((attribute != NULL) && (attribute->attrValuesNumber != 0)) > { > >>> + /* scAbsenceAllowed has value. Get the value */ > >>> + value = attribute->attrValues[0]; > >>> + *attr_val = *(static_cast<SaUint32T *>(value)); > >>> + rc_attr_val = attr_val; > >>> + } > >>> + > >>> +done_fin_Om: > >>> + /* Free Om resources */ > >>> + ais_rc = immutil_saImmOmFinalize(immOmHandle); > >>> + if (ais_rc != SA_AIS_OK) { > >>> + TRACE("%s\t saImmOmFinalize Fail '%s'", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + } > >>> + > >>> +done: > >>> + /* Restore immutil settings */ > >>> + immutilWrapperProfile.errorsAreFatal = > >>> tmp_immutilWrapperProfile.errorsAreFatal; > >>> + immutilWrapperProfile.nTries = > tmp_immutilWrapperProfile.nTries; > >>> + immutilWrapperProfile.retryInterval = > >>> tmp_immutilWrapperProfile.retryInterval; > >>> + > >>> + TRACE_LEAVE(); > >>> + return rc_attr_val; > >>> +} > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_main.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_main.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_main.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_main.cc > >>> @@ -31,21 +31,26 @@ > >>> #include <daemon.h> > >>> #include <nid_api.h> > >>> #include <ncs_main_papi.h> > >>> +#include <osaf_time.h> > >>> > >>> #include "lgs.h" > >>> #include "lgs_file.h" > >>> #include "osaf_utility.h" > >>> +#include "lgs_recov.h" > >>> > >>> /* > >>> > >>================================================================= > >= > >>====== > >>> * DEFINITIONS > >>> * > >>> > >>================================================================= > >= > >>====== > >>> */ > >>> - > >>> -#define FD_TERM 0 > >>> -#define FD_AMF 1 > >>> -#define FD_MBCSV 2 > >>> -#define FD_MBX 3 > >>> -#define FD_IMM 4 /* Must be the last in the fds array */ > >>> +enum { > >>> + FD_TERM = 0, > >>> + FD_AMF, > >>> + FD_MBCSV, > >>> + FD_MBX, > >>> + FD_CLTIMER, > >>> + FD_IMM, /* Must be the last in the fds array */ > >>> + FD_NUM > >>> +}; > >>> > >>> #ifndef LOG_STREAM_LOW_LIMIT_PERCENT > >>> #define LOG_STREAM_LOW_LIMIT_PERCENT 0.6 // default value for low > is > >>> 60% > >>> @@ -62,6 +67,7 @@ > >>> */ > >>> > >>> static lgs_cb_t _lgs_cb; > >>> + > >>> lgs_cb_t *lgs_cb = &_lgs_cb; > >>> SYSF_MBX lgs_mbx; /* LGS's mailbox */ > >>> > >>> @@ -83,8 +89,8 @@ uint32_t mbox_low[NCS_IPC_PRIORITY_MAX]; > >>> */ > >>> pthread_mutex_t lgs_mbox_init_mutex = PTHREAD_MUTEX_INITIALIZER; > >>> > >>> -static struct pollfd fds[5]; > >>> -static nfds_t nfds = 5; > >>> +static struct pollfd fds[FD_NUM]; > >>> +static nfds_t nfds = FD_NUM; > >>> static NCS_SEL_OBJ usr1_sel_obj; > >>> > >>> /** > >>> @@ -195,6 +201,69 @@ uint32_t lgs_configure_mailbox(void) > >>> } > >>> > >>> /** > >>> + * Get OpenSAF global scAbsenceAllowed configuration > >>> + */ > >>> +static void init_scAbsenceAllowed() > >>> +{ > >>> + SaUint32T scAbsenceAllowed_val = 0; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + if (lgs_get_scAbsenceAllowed_attr(&scAbsenceAllowed_val) != > NULL) { > >>> + TRACE("%s\t Got scAbsenceAllowed_val = %d", __FUNCTION__, > >>> + scAbsenceAllowed_val); > >>> + } > >>> + > >>> + lgs_cb->scAbsenceAllowed = scAbsenceAllowed_val; > >>> + > >>> + TRACE_LEAVE(); > >>> +} > >>> + > >>> +/** > >>> + * Initiate recovery handling. > >>> + * Note1: This must be done during init before any streams are > >>> created > >>> + * Note2: Recovery state timer is started in the main() function > >>> + * > >>> + * If absence is allowed configuration is set: > >>> + * - Create a list of recoverable streams > >>> + * - If list is not empty set recovery state to LGS_RECOVERY > >>> + */ > >>> +static void init_recovery() > >>> +{ > >>> + int list_num = 0; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + /* Set default state */ > >>> + lgs_cb->lgs_recovery_state = LGS_NORMAL; > >>> + > >>> + /* Get the value of the scAbsenceAllowed IMM attribute for > >>> configuring > >>> + * SC node absence handling > >>> + */ > >>> + init_scAbsenceAllowed(); > >>> + > >>> + if (lgs_cb->scAbsenceAllowed == 0) { > >>> + TRACE("%s Absence is not allowded", __FUNCTION__); > >>> + TRACE_LEAVE(); > >>> + return; > >>> + } > >>> + > >>> + /* Create a list of recoverable (IMM object exist) streams */ > >>> + lgs_search_stream_objects(); > >>> + list_num = log_rtobj_list_no(); > >>> + > >>> + TRACE("Number of runtime objects found = %d", list_num); > >>> + > >>> + /* set recovery state if the list is not empty */ > >>> + if (list_num != 0) { > >>> + /* There are objects in list */ > >>> + lgs_cb->lgs_recovery_state = LGS_RECOVERY; > >>> + } > >>> + > >>> + TRACE_LEAVE2(); > >>> +} > >>> + > >>> +/** > >>> * Initialize log > >>> * > >>> * @return uns32 > >>> @@ -254,6 +323,9 @@ static uint32_t log_initialize(void) > >>> goto done; > >>> } > >>> > >>> + /* Initiate "headless" recovery handling */ > >>> + init_recovery(); > >>> + > >>> /* Initialize configuration stream class > >>> * Configuration must have been initialized > >>> */ > >>> @@ -324,7 +396,11 @@ static uint32_t log_initialize(void) > >>> /* Create streams that has configuration objects and become > >>> * class implementer for the SaLogStreamConfig class > >>> */ > >>> - if (lgs_imm_create_configStream(lgs_cb) != SA_AIS_OK) { > >>> + > >>> + /* Note1: lgs_cb->immOiHandle is set in lgs_imm_init() > >>> + * Note2: lgs_cb->logsv_root_dir must be set > >>> + */ > >>> + if (lgs_imm_init_configStreams(lgs_cb) != SA_AIS_OK) { > >>> LOG_ER("lgs_imm_create_configStream FAILED"); > >>> rc = NCSCC_RC_FAILURE; > >>> goto done; > >>> @@ -362,6 +438,18 @@ int main(int argc, char *argv[]) > >>> uint32_t rc; > >>> int term_fd; > >>> > >>> + /* File descriptor for timeout to delete not used stream > runtime > >>> objects. > >>> + * May exist after 'headless' state. > >>> + */ > >>> + int cltimer_fd = -1; > >>> + > >>> + /* Timeout time in seconds before clean up of runtime objects > that > >>> may > >>> + * still exist after a "headless" situation. After timeout > recovery > >>> of > >>> + * "lost" streams is no longer possible. > >>> + */ > >>> + const time_t CLEAN_TIMEOUT = 600; /* 10 min */ > >>> + //const time_t CLEAN_TIMEOUT = 60; /* 1 min LLDTEST */ > >>> + > >>> daemonize(argc, argv); > >>> > >>> if (log_initialize() != NCSCC_RC_SUCCESS) { > >>> @@ -372,6 +460,13 @@ int main(int argc, char *argv[]) > >>> mbx_fd = ncs_ipc_get_sel_obj(&lgs_mbx); > >>> daemon_sigterm_install(&term_fd); > >>> > >>> + if (log_rtobj_list_no() != 0) { > >>> + /* Needed only if any "lost" objects are found > >>> + * See log_initialize */ > >>> + cltimer_fd = lgs_init_timer(CLEAN_TIMEOUT); > >>> + TRACE("%s Recovery timeout started", __FUNCTION__); > >>> + } > >>> + > >>> /* Set up all file descriptors to listen to */ > >>> fds[FD_TERM].fd = term_fd; > >>> fds[FD_TERM].events = POLLIN; > >>> @@ -382,6 +477,8 @@ int main(int argc, char *argv[]) > >>> fds[FD_MBCSV].events = POLLIN; > >>> fds[FD_MBX].fd = mbx_fd.rmv_obj; > >>> fds[FD_MBX].events = POLLIN; > >>> + fds[FD_CLTIMER].fd = cltimer_fd; > >>> + fds[FD_CLTIMER].events = POLLIN; > >>> fds[FD_IMM].fd = lgs_cb->immSelectionObject; > >>> fds[FD_IMM].events = POLLIN; > >>> > >>> @@ -440,6 +537,25 @@ int main(int argc, char *argv[]) > >>> } > >>> } > >>> > >>> + if (fds[FD_CLTIMER].revents & POLLIN) { > >>> + /* To avoid 'stray objects', after a timeout all runtime > >>> + * objects that has not been restored shall be deleted > >>> + */ > >>> + TRACE("Recover state End. Clean objects"); > >>> + > >>> + /* Close timer to free resources and stop timer poll */ > >>> + lgs_close_timer(cltimer_fd); > >>> + fds[FD_CLTIMER].fd = -1; > >>> + > >>> + if (lgs_cb->ha_state == SA_AMF_HA_ACTIVE) { > >>> + /* Delete objects left if active */ > >>> + lgs_clean_stream_objects(); > >>> + } > >>> + > >>> + /* Remove the found objects list */ > >>> + log_rtobj_list_free(); > >>> + } > >>> + > >>> if (fds[FD_MBX].revents & POLLIN) > >>> lgs_process_mbx(&lgs_mbx); > >>> > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_mbcsv.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_mbcsv.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_mbcsv.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_mbcsv.cc > >>> @@ -24,6 +24,7 @@ > >>> #include "lgs_mbcsv_v3.h" > >>> #include "lgs_mbcsv_v2.h" > >>> #include "lgs_mbcsv_v1.h" > >>> +#include "lgs_recov.h" > >>> > >>> /* > >>> LGS_CKPT_DATA_HEADER > >>> @@ -1856,6 +1857,7 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > >>> { > >>> lgs_ckpt_stream_open_t *param; > >>> log_stream_t *stream; > >>> + int pos = 0; > >>> > >>> TRACE_ENTER(); > >>> > >>> @@ -1871,7 +1873,8 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > >>> /* Check that client still exist */ > >>> if ((param->clientId != invalidClient) && > >>> (lgs_client_get_by_id(param->clientId) == NULL)) { > >>> - LOG_WA("\tClient %u does not exist, failed to create stream > >>'%s'", > >>> param->clientId, param->logStreamName); > >>> + LOG_WA("\tClient %u does not exist, failed to create stream > >>'%s'", > >>> + param->clientId, param->logStreamName); > >>> goto done; > >>> } > >>> > >>> @@ -1890,7 +1893,7 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > >>> strcpy((char *)name.value, param->logStreamName); > >>> name.length = strlen(param->logStreamName); > >>> > >>> - stream = log_stream_new(&name, > >>> + stream = log_stream_new_1(&name, > >>> param->logFile, > >>> param->logPath, > >>> param->maxFileSize, > >>> @@ -1901,11 +1904,12 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > >>> param->streamType, > >>> param->streamId, > >>> SA_FALSE, // FIX sync or calculate? > >>> - param->logRecordId); > >>> + param->logRecordId, > >>> + 0); > >>> > >>> if (stream == NULL) { > >>> /* Do not allow standby to get out of sync */ > >>> - LOG_ER("%s - Failed to create stream > >>'%s'",__FUNCTION__, > >>> + LOG_ER("%s - Failed to create stream '%s'", > >>__FUNCTION__, > >>> param->logStreamName); > >>> goto done; > >>> } > >>> @@ -1923,6 +1927,9 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > >>> */ > >>> if (lgs_is_split_file_system()) { > >>> if (stream->numOpeners <= 1) { > >>> + TRACE("%s: log_initiate_stream_files(%s)", > >>> + __FUNCTION__, stream->fileName.c_str()); > >>> + > >>> log_initiate_stream_files(stream); > >>> } > >>> } > >>> @@ -1935,11 +1942,16 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t > >>> if ((param->clientId != invalidClient) && > >>> lgs_client_stream_add(param->clientId, stream->streamId) != > >>0) { > >>> /* Do not allow standby to get out of sync */ > >>> - LOG_ER("%s - Failed to add stream '%s' to client > >>%u",__FUNCTION__, > >>> + LOG_ER("%s - Failed to add stream '%s' to client %u", > >>> __FUNCTION__, > >>> param->logStreamName, param->clientId); > >>> lgs_exit("Could not add stream to client", > >>> SA_AMF_COMPONENT_RESTART); > >>> } > >>> > >>> + /* Stream is opened on standby. Remove from rtobj list if exist > */ > >>> + pos = log_rtobj_list_find(param->logStreamName); > >>> + if (pos != -1) > >>> + log_rtobj_list_erase_one_pos(pos); > >>> + > >>> done: > >>> /* Free strings allocated by the EDU encoder */ > >>> lgs_free_edu_mem(param->logFile); > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_recov.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_recov.cc > >>> new file mode 100644 > >>> --- /dev/null > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_recov.cc > >>> @@ -0,0 +1,758 @@ > >>> +/* -*- OpenSAF -*- > >>> + * > >>> + * (C) Copyright 2016 The OpenSAF Foundation > >>> + * > >>> + * This program is distributed in the hope that it will be > useful, > >>> but > >>> + * WITHOUT ANY WARRANTY; without even the implied warranty of > >>> MERCHANTABILITY > >>> + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program > are > >>> licensed > >>> + * under the GNU Lesser General Public License Version 2.1, > February > >>> 1999. > >>> + * The complete license can be accessed from the following > location: > >>> + * http://opensource.org/licenses/lgpl-license.php > >>> + * See the Copying file included with the OpenSAF distribution > for > >>> full > >>> + * licensing terms. > >>> + * > >>> + * Author(s): Ericsson AB > >>> + * > >>> + */ > >>> + > >>> +#include "lgs_recov.h" > >>> +#include "lgs_file.h" > >>> +#include "lgs_filehdl.h" > >>> + > >>> +/*** > >>> + * The following functions are used to handle a list of runtime > >>> stream objects > >>> + * (application streams with runtime objects) that may be found > >>> during start of > >>> + * the log server. > >>> + * The list consists of a vector of strings containing runtime > stream > >>> object > >>> + * dn:s > >>> + */ > >>> + > >>> +static char **rtobj_list = NULL; > >>> +static bool rtobj_list_init_f = false; > >>> +static uint32_t rtobj_cnt = 0; /* Number of object names in list > */ > >>> +static SaUint32T rtobj_list_len = 0; /* Also the length of the > list > >>> */ > >>> + > >>> +/** > >>> + * Initiate the list by allocating memory for a vector of > pointers to > >>> strings > >>> + * (char *). The number of elements will be > >>> "logMaxApplicationStreams", see log > >>> + * configuration object. > >>> + * > >>> + * @return true if initiated (list exist) > >>> + */ > >>> +static bool log_rtobj_list_init() > >>> +{ > >>> + TRACE_ENTER(); > >>> + > >>> + if (rtobj_list_init_f == true) > >>> + goto done; /* Already initiated */ > >>> + > >>> + rtobj_list_len = *static_cast<const SaUint32T *>( > >>> + lgs_cfg_get(LGS_IMM_LOG_MAX_APPLICATION_STREAMS)); > >>> + > >>> + rtobj_list = static_cast<char **>(calloc(rtobj_list_len, > sizeof(char > >>> *))); > >>> + if (rtobj_list != NULL) { > >>> + rtobj_list_init_f = true; > >>> + } else { > >>> + TRACE("%s\tFail to alloc memory for rtobj_list", > >>__FUNCTION__); > >>> + } > >>> + > >>> +done: > >>> + TRACE_LEAVE2("rtobj_list_init_f = %d", rtobj_list_init_f); > >>> + return rtobj_list_init_f; > >>> +} > >>> + > >>> +/** > >>> + * Free an initialized rtobj list > >>> + * > >>> + */ > >>> +void log_rtobj_list_free() > >>> +{ > >>> + TRACE_ENTER(); > >>> + > >>> + if (rtobj_list == NULL) > >>> + return; /* No list to free */ > >>> + > >>> + /* Free positions in the list if needed */ > >>> + uint32_t pos; > >>> + for (pos = 0; pos < rtobj_list_len; pos++) { > >>> + if (rtobj_list[pos] != NULL) > >>> + free(rtobj_list[pos]); > >>> + } > >>> + > >>> + free(rtobj_list); > >>> + rtobj_cnt = 0; > >>> + rtobj_list_len = 0; > >>> + rtobj_list = NULL; > >>> + > >>> + TRACE_LEAVE(); > >>> +} > >>> + > >>> +/** > >>> + * Add a dn name of an rt-object > >>> + * > >>> + * @param dn_str[in] '\0' terminated string containing a dn > >>> + * @return -1 on error > >>> + */ > >>> +int log_rtobj_list_add(char *dn_str) > >>> +{ > >>> + char *str_ptr = NULL; > >>> + size_t len = 0; > >>> + int rc = 0; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + if (log_rtobj_list_init() == false) { > >>> + TRACE("%s\trtobj_list not initiated!", __FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + if (rtobj_cnt >= rtobj_list_len) { > >>> + /* Should be impossible */ > >>> + LOG_WA("%s\trtobj_cnt >= maxApplicationStreams!", > >>> + __FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + /* Save dn string */ > >>> + len = strlen(dn_str) + 1; /* Including '\0' */ > >>> + if (len > SA_MAX_NAME_LENGTH) { > >>> + /* Should never happen */ > >>> + LOG_WA("%s\tToo long dn string!",__FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + str_ptr = static_cast<char *>(calloc(1, len)); > >>> + if (str_ptr == NULL) { > >>> + LOG_WA("%s\tcalloc Fail",__FUNCTION__); > >>> + rc = -1; > >>> + goto done; > >>> + } > >>> + > >>> + strcpy(str_ptr, dn_str); > >>> + > >>> + /* Add dn to list */ > >>> + rtobj_list[rtobj_cnt] = str_ptr; > >>> + rtobj_cnt++; > >>> + > >>> + done: > >>> + TRACE_LEAVE2("rc = %d", rc); > >>> + return rc; > >>> +} > >>> + > >>> +/** > >>> + * Return number of names in list > >>> + * > >>> + * @return Number of names > >>> + */ > >>> +int log_rtobj_list_no() > >>> +{ > >>> + return rtobj_cnt; > >>> +} > >>> + > >>> +/** > >>> + * Find pos of the given given name in list > >>> + * > >>> + * @param dn_str[in] '\0' terminated string with dn to find > >>> + * @return Position of found name or -1 if name not found > >>> + */ > >>> +int log_rtobj_list_find(char *dn_str) > >>> +{ > >>> + uint32_t i = 0; > >>> + int pos = -1; > >>> + > >>> + TRACE_ENTER2("dn_str \"%s\"", dn_str); > >>> + > >>> + if (rtobj_list == NULL) { > >>> + TRACE("\t No rtobj_list exist"); > >>> + goto done; > >>> + } > >>> + > >>> + for (i = 0; i < rtobj_list_len; i++) { > >>> + if (rtobj_list[i] == NULL) > >>> + continue; > >>> + if (strcmp(rtobj_list[i], dn_str) == 0) { > >>> + /* Found! */ > >>> + pos = (int) i; > >>> + break; > >>> + } > >>> + } > >>> + > >>> +done: > >>> + TRACE_LEAVE2("pos = %d", pos); > >>> + return pos; > >>> +} > >>> + > >>> +/** > >>> + * Get pos of first name found in list. > >>> + * > >>> + * @return Position of found name or -1 if no name found > >>> + */ > >>> +int log_rtobj_list_getnamepos() > >>> +{ > >>> + uint32_t i = 0; > >>> + int pos = -1; > >>> + > >>> + for (i = 0; i < rtobj_list_len; i++) { > >>> + if (rtobj_list[i] == NULL) { > >>> + continue; > >>> + } else { > >>> + pos = static_cast<int>(i); > >>> + break; > >>> + } > >>> + } > >>> + > >>> + return pos; > >>> +} > >>> + > >>> +/** > >>> + * Get name at given pos in list. > >>> + * > >>> + * @param pos[in] Position for name to get > >>> + * @return A pointer to the string in given pos. > >>> + * If there is no string in pos or pos is outside the > list > >>> + * NULL is returned. > >>> + */ > >>> +char *log_rtobj_list_getname(int pos) > >>> +{ > >>> + if (pos >= static_cast<int>(rtobj_list_len)) > >>> + return NULL; > >>> + > >>> + return rtobj_list[pos]; > >>> +} > >>> + > >>> +/** > >>> + * "Erase" dn name at pos in list > >>> + * > >>> + * @param pos[in] Position in list to erase > >>> + */ > >>> +void log_rtobj_list_erase_one_pos(int pos) > >>> +{ > >>> + TRACE_ENTER2("pos = %d", pos); > >>> + if (pos > static_cast<int>(rtobj_list_len)) { > >>> + TRACE_LEAVE2("%s\tPosition %d outside list!", __FUNCTION__, > >>pos); > >>> + return; > >>> + } > >>> + > >>> + if (rtobj_list[pos] == NULL) { > >>> + TRACE_LEAVE2("%s\tNo string in pos %d", __FUNCTION__, > >>pos); > >>> + return; > >>> + } > >>> + > >>> + free(rtobj_list[pos]); > >>> + rtobj_list[pos] = NULL; > >>> + > >>> + if (rtobj_cnt > 0) > >>> + rtobj_cnt--; > >>> + > >>> + TRACE_LEAVE(); > >>> +} > >>> + > >>> +/*** > >>> + * Restore a lost runtime stream. > >>> + * Get attributes from given runtime object (if exists). > >>> + * Find the last open logfile. From the file get number of log > >>> records written > >>> + * and latest used log record id > >>> + * If fail delete the stream IMM object (if exist) > >>> + * Always delete the object name from the rtobj list > >>> + */ > >>> + > >>> +/** > >>> + * Local help function > >>> + * Find the file that was current log file before server down. > >>> + * Get file size > >>> + * Get last written record Id by reading the first characters of > the > >>> last > >>> + * log record in the file > >>> + * > >>> + * NOTE: > >>> + * All file handling must be done in the file thread > >>> + * See lgs_get_file_params_hdl() in lgs_filehdl.c > >>> + * > >>> + * @param fileName[in] The name in saLogStreamFileName e.g. > "app1" > >>> + * @param pathName[in] Full path to the dir where the log file > can > >>> be found > >>> + * root path + relative path > >>> + * @param logFileCurrent[out] > >>> + * @param curFileSize[out] > >>> + * @param logRecordId[out] > >>> + * > >>> + * @return -1 on error > >>> + */ > >>> +//static int lgs_get_file_params_h(gfp_in_t *par_in, gfp_out_t > >>> *par_out) > >>> +static int lgs_get_file_params_h(gfp_in_t *par_in, gfp_out_t > >>> *par_out) > >>> +{ > >>> + lgsf_retcode_t api_rc; > >>> + lgsf_apipar_t apipar; > >>> + int rc = 0; > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + /* Fill in API structure */ > >>> + apipar.req_code_in = LGSF_GET_FILE_PAR; > >>> + apipar.data_in_size = sizeof(gfp_in_t); > >>> + apipar.data_in = par_in; > >>> + apipar.data_out_size = sizeof(gfp_out_t); > >>> + apipar.data_out = par_out; > >>> + > >>> + api_rc = log_file_api(&apipar); > >>> + if (api_rc != LGSF_SUCESS) { > >>> + TRACE("%s - API error %s", __FUNCTION__, > >>> + lgsf_retcode_str(api_rc)); > >>> + rc = -1; > >>> + } else { > >>> + rc = apipar.hdl_ret_code_out; > >>> + TRACE("\t hdl_ret_code_out = %d", rc); > >>> + } > >>> + > >>> + TRACE_LEAVE(); > >>> + return rc; > >>> +} > >>> + > >>> +static void lgs_remove_stream(uint32_t client_id, log_stream_t > >>> *log_stream) > >>> +{ > >>> + TRACE_ENTER(); > >>> + > >>> + /* Remove the stream handle and > >>> + * remove the stream resources > >>> + */ > >>> + if (lgs_client_stream_rmv(client_id, log_stream->streamId) > < > >>> 0) { > >>> + TRACE("%s lgs_client_stream_rmv Fail", __FUNCTION__); > >>> + } > >>> + > >>> + log_free_stream_resources(log_stream); > >>> + > >>> + log_stream = NULL; > >>> + > >>> + TRACE_LEAVE(); > >>> +} > >>> + > >>> +/** > >>> + * Restore parameters for a lost runtime (application) stream, > create > >>> the > >>> + * stream in the local stream list and associate with a client. > >>> + * Remove the stream object name from the found objects list. > >>> + * > >>> + * @param stream_name[in] The name of the stream > >>> + * @param client_id[in] Client to recover stream for > >>> + * @param o_stream[out] The recovered stream > >>> + * stream > >>> + * @return -1 on error > >>> + */ > >>> +int lgs_restore_one_app_stream( > >>> + char *stream_name, uint32_t client_id, > >>> + log_stream_t **o_stream) > >>> +{ > >>> + int int_rc = 0; > >>> + int rc_out = 0; > >>> + SaAisErrorT ais_rc = SA_AIS_OK; > >>> + SaImmHandleT immOmHandle; > >>> + SaImmAttrValuesT_2 *attribute; > >>> + SaImmAttrValuesT_2 **attributes; > >>> + gfp_in_t par_in; > >>> + gfp_out_t par_out; > >>> + int list_pos; > >>> + int n; > >>> + int i = 0; > >>> + > >>> + lgsv_stream_open_req_t open_stream_param; > >>> + log_stream_t *log_stream = NULL; > >>> + SaTimeT restored_creationTimeStamp = 0; > >>> + SaUint32T restored_severityFilter = 0; > >>> + > >>> + std::string fileName; > >>> + std::string pathName; > >>> + > >>> + // Make it safe for free > >>> + par_out.curFileName = NULL; > >>> + > >>> + TRACE_ENTER2("object_name \"%s\", client_id=%d", stream_name, > >>> client_id); > >>> + > >>> + memset(&open_stream_param, 0, sizeof(open_stream_param)); > >>> + > >>> + /* Check and save stream file name */ > >>> + if (stream_name == NULL) { > >>> + TRACE("%s: No object name <NULL>", __FUNCTION__); > >>> + rc_out = -1; > >>> + goto done; > >>> + } > >>> + n = snprintf(reinterpret_cast<char > >>> *>(open_stream_param.lstr_name.value), > >>> + SA_MAX_NAME_LENGTH, "%s", stream_name); > >>> + > >>> + open_stream_param.lstr_name.length = strlen(stream_name) + 1; > >>> + if (n >= SA_MAX_NAME_LENGTH) { > >>> + TRACE("Log stream name \"%s\" is truncated", > >>> + open_stream_param.lstr_name.value); > >>> + rc_out = -1; > >>> + goto done; > >>> + } > >>> + > >>> + /* Check if in found objects list */ > >>> + list_pos = log_rtobj_list_find(stream_name); > >>> + if (list_pos == -1) { > >>> + TRACE("%s: No stream \"%s\" found to restore", > >>__FUNCTION__, > >>> stream_name); > >>> + rc_out = -1; > >>> + goto done; > >>> + } > >>> + > >>> + /* Remove from list */ > >>> + log_rtobj_list_erase_one_pos(list_pos); > >>> + > >>> + /* Get the cached attributes for the object > >>> + */ > >>> + int_rc = lgs_get_streamobj_attr(&attributes, stream_name, > >>> &immOmHandle); > >>> + if (int_rc == -1) { > >>> + TRACE("%s:\t lgs_get_streamobj_attr Fail", __FUNCTION__); > >>> + rc_out = -1; > >>> + goto done; > >>> + } > >>> + > >>> + /* Fill in the "lgsv_stream_open_req_t open_stream_param" > struct > >>> with > >>> + * recovered parameters, re-create the stream and add it to the > >>> client > >>> + * Note: Before restoring a stream the client to own the stream > >>> must > >>> + * exist. > >>> + */ > >>> + i = 0; > >>> + while ((attribute = attributes[i++]) != NULL) { > >>> + void *value; > >>> + char *name; > >>> + char *str_val; > >>> + > >>> + /* Fill in parameters read from the app stream object > >>> + */ > >>> + name = attribute->attrName; > >>> + if (attribute->attrValuesNumber != 0) > >>> + value = attribute->attrValues[0]; > >>> + else { > >>> + value = NULL; > >>> + } > >>> + > >>> + TRACE("\t attribute name \"%s\"", name); > >>> + if (!strcmp(name, "saLogStreamFileName")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + > >>> + fileName = *(static_cast<char **>(value)); > >>> + TRACE("\t saLogStreamFileName \"%s\"", > >>fileName.c_str()); > >>> + } else if (!strcmp(name, "saLogStreamPathName")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + > >>> + pathName = *(static_cast<char **>(value)); > >>> + TRACE("\t saLogStreamPathName \"%s\"", > >>pathName.c_str()); > >>> + } else if (!strcmp(name, "saLogStreamMaxLogFileSize")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + > >>> + open_stream_param.maxLogFileSize = > >>*(static_cast<SaUint64T > >>> *>(value)); > >>> + TRACE("\t saLogStreamMaxLogFileSize = %lld", > >>> + open_stream_param.maxLogFileSize); > >>> + > >>> + } else if (!strcmp(name, "saLogStreamFixedLogRecordSize")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + open_stream_param.maxLogRecordSize = > >>*(static_cast<SaUint32T > >>> *>(value)); > >>> + TRACE("\t saLogStreamFixedLogRecordSize = %d", > >>> + open_stream_param.maxLogRecordSize); > >>> + > >>> + } else if (!strcmp(name, "saLogStreamHaProperty")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + open_stream_param.haProperty = > >>*(static_cast<SaBoolT *>(value)); > >>> + TRACE("\t saLogStreamHaProperty=%d", > >>> + open_stream_param.haProperty); > >>> + > >>> + } else if (!strcmp(name, "saLogStreamLogFullAction")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + open_stream_param.logFileFullAction = > >>> *(static_cast<SaLogFileFullActionT *>(value)); > >>> + TRACE("\t saLogStreamLogFullAction=%d", > >>> + open_stream_param.logFileFullAction); > >>> + > >>> + } else if (!strcmp(name, "saLogStreamMaxFilesRotated")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + open_stream_param.maxFilesRotated = > >>*(static_cast<SaUint16T > >>> *>(value)); > >>> + TRACE("\t saLogStreamMaxFilesRotated=%d", > >>> + open_stream_param.maxFilesRotated); > >>> + > >>> + } else if (!strcmp(name, "saLogStreamLogFileFormat")) { > >>> + /*TBD Is this correct way of handling format? > >>> + * - Must be checked! > >>> + * - Use better max length than PATH_MAX! > >>> + */ > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + str_val = *(static_cast<char **>(value)); > >>> + open_stream_param.logFileFmt = NULL; > >>> + open_stream_param.logFileFmt = static_cast<char *>( > >>> + calloc(1, strlen(str_val) + 1)); > >>> + if (open_stream_param.logFileFmt == NULL) { > >>> + TRACE("%s [%d] calloc Fail", > >>> + __FUNCTION__, __LINE__); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + n = snprintf(open_stream_param.logFileFmt, > >>> + PATH_MAX, "%s", str_val); > >>> + open_stream_param.logFileFmtLength = > >>> + strlen(open_stream_param.logFileFmt); > >>> + if (n >= PATH_MAX) { > >>> + TRACE("Format string \"%s\" too long", > >>> + str_val); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + TRACE("\t saLogStreamLogFileFormat \"%s\"", > >>> + open_stream_param.logFileFmt); > >>> + > >>> + } else if (!strcmp(name, "saLogStreamCreationTimestamp")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + restored_creationTimeStamp = *(static_cast<SaTimeT > >>*>(value)); > >>> + TRACE("\t saLogStreamCreationTimestamp=%lld", > >>> + restored_creationTimeStamp); > >>> + > >>> + } else if (!strcmp(name, "saLogStreamSeverityFilter")) { > >>> + if (value == NULL) { > >>> + TRACE("%s: Fail, has empty value", name); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + restored_severityFilter = *(static_cast<SaUint32T > >>*>(value)); > >>> + TRACE("\t saLogStreamSeverityFilter=%d", > >>> + restored_severityFilter); > >>> + } > >>> + } > >>> + > >>> + /* Fill in the rest of the stream open parameters > >>> + * and create the stream. Do not create an IMM object > >>> + */ > >>> + open_stream_param.client_id = client_id; > >>> + open_stream_param.lstr_open_flags = 0; /* Dummy not used here > */ > >>> + > >>> + open_stream_param.logFileName = const_cast<char > >>> *>(fileName.c_str()); > >>> + open_stream_param.logFilePathName = const_cast<char > >>> *>(pathName.c_str()); > >>> + > >>> + ais_rc = create_new_app_stream(&open_stream_param, &log_stream, > >>0); > >>> + if ( ais_rc != SA_AIS_OK) { > >>> + TRACE("%s: create_new_app_stream Fail %s", > >>> + __FUNCTION__, saf_error(ais_rc)); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + > >>> + /* Create an association between this client and the stream */ > >>> + int_rc = lgs_client_stream_add(client_id, > log_stream->streamId); > >>> + if (int_rc == -1) { > >>> + TRACE("%s: lgs_client_stream_add Fail", __FUNCTION__); > >>> + log_free_stream_resources(log_stream); /* Undo create new > >>stream > >>> */ > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + > >>> + /* Set values for attributes not restored when creating a > stream. > >>> + * The value must be set after the stream is created. > >>> + * Cached: > >>> + * - saLogStreamSeverityFilter (cached) > >>> + * - saLogStreamCreationTimestamp (cached) > >>> + * > >>> + * Pure runtime: > >>> + * - saLogStreamNumOpeners > >>> + * Handled when streams are opened. No recovery needed > >>> + * - logStreamDiscardedCounter > >>> + * Cannot be restored. Initialized with 0 > >>> + */ > >>> + log_stream->creationTimeStamp = restored_creationTimeStamp; > >>> + log_stream->severityFilter = restored_severityFilter; > >>> + log_stream->filtered = 0; > >>> + > >>> + TRACE("\t Stream obj attributes handled and stream is > created"); > >>> + > >>> + /* Get recovery data from the log file and update the stream > >>> + */ > >>> + pathName = static_cast<const char *>( > >>> + lgs_cfg_get(LGS_IMM_LOG_ROOT_DIRECTORY)); > >>> + pathName = pathName + "/" + open_stream_param.logFilePathName; > >>> + > >>> + par_in.file_name = open_stream_param.logFileName; > >>> + par_in.file_path = const_cast<char *>(pathName.c_str()); > >>> + > >>> + int_rc = lgs_get_file_params_h(&par_in, &par_out); > >>> + if (int_rc == -1) { > >>> + TRACE("%s:\t lgs_get_file_params_h Fail", __FUNCTION__); > >>> + /* Remove the stream handle and > >>> + * remove the stream resources > >>> + */ > >>> + lgs_remove_stream(client_id, log_stream); > >>> + rc_out = -1; > >>> + goto done_free_attr; > >>> + } > >>> + > >>> + /* If no current log file create a file name > >>> + */ > >>> + if (par_out.curFileName == NULL) { > >>> + /* There is no current log file. Create a file name */ > >>> + log_stream->logFileCurrent = log_stream->fileName + "_" + > >>> lgs_get_time(NULL); > >>> + TRACE("\t A new file name for current log file is created"); > >>> + } else { > >>> + log_stream->logFileCurrent = par_out.curFileName; > >>> + } > >>> + > >>> + TRACE("\t Current log file \"%s\"", > >>> log_stream->logFileCurrent.c_str()); > >>> + > >>> + log_stream->curFileSize = par_out.curFileSize; > >>> + log_stream->logRecordId = par_out.logRecordId; > >>> + > >>> + /* The stream is open again and opened for the first time so far > */ > >>> + log_stream->numOpeners = 1; > >>> + > >>> + /* Set the stream file descriptor to -1. The file will then be > >>> opened > >>> + * at next write. > >>> + */ > >>> + *log_stream->p_fd = -1; > >>> + > >>> +done_free_attr: > >>> + /* Free resources used for finding attribute values */ > >>> + int_rc = lgs_free_streamobj_attr(immOmHandle); > >>> + if (int_rc == -1) { > >>> + TRACE("%s:\t lgs_free_streamobj_attr Fail", __FUNCTION__); > >>> + rc_out = -1; > >>> + } > >>> + > >>> +done: > >>> + free(open_stream_param.logFileFmt); > >>> + if (par_out.curFileName != NULL) { > >>> + // This memory is allocated in lgs_get_file_params_hdl() > >>> + free(par_out.curFileName); > >>> + } > >>> + > >>> + /* Return recovered stream. Will be NULL if not recovered > (rc_out = > >>> -1) */ > >>> + *o_stream = log_stream; > >>> + TRACE_LEAVE2("rc_out = %d", rc_out); > >>> + return rc_out; > >>> +} > >>> + > >>> +/** > >>> + * Restore lost files for a configuration stream. > >>> + * Update the stream with current log file and log record Id > >>> + * > >>> + * > >>> + * @param log_stream[in/out] > >>> + * Will/May modify the following stream parameters: > >>> + * logFileCurrent > >>> + * curFileSize > >>> + * logRecordId > >>> + * creationTimeStamp > >>> + * *p_fd > >>> + * numOpeners > >>> + * > >>> + * @return -1 on error > >>> + */ > >>> +int log_stream_open_file_restore(log_stream_t *stream) > >>> +{ > >>> + int int_rc = 0; > >>> + int rc_out = 0; > >>> + gfp_in_t par_in; > >>> + gfp_out_t par_out; > >>> + std::string pathName, log_root_path; > >>> + size_t name_length = lgs_max_nlength(); > >>> + > >>> + TRACE_ENTER(); > >>> + > >>> + pathName = static_cast<const char > >>> *>(lgs_cfg_get(LGS_IMM_LOG_ROOT_DIRECTORY)); > >>> + pathName = pathName + "/" + stream->pathName; > >>> + > >>> + par_in.file_name = const_cast<char > *>(stream->fileName.c_str()); > >>> + par_in.file_path = const_cast<char *>(pathName.c_str()); > >>> + > >>> + TRACE("pathName = %s, fileName = %s, name_length = %zu", > >>> + pathName.c_str(), stream->fileName.c_str(), name_length); > >>> + > >>> + // Initialize the output > >>> + par_out.curFileSize = 0; > >>> + par_out.logRecordId = 0; > >>> + par_out.curFileName = NULL; > >>> + > >>> + int_rc = lgs_get_file_params_h(&par_in, &par_out); > >>> + > >>> + /**** > >>> + * Rules for lgs_get_file_params_h(): > >>> + * - If current log file not empty: Name = cur log file, Size = > file > >>> size, > >>> + * Id = fr file, rc = OK > >>> + * - If current log file empty no rotated: Name = cur log file, > Size > >>> = 0, > >>> + * Id = 1, rc = OK > >>> + * - If current log file empty is rotated: Name = cur log file, > Size > >>> = 0, > >>> + * Id = fr last rotated, > rc > >>> = OK > >>> + * - If no log file at all: Name = NULL, Size = 0, Id = 1, rc = > OK > >>> + * - If only rotated log file: Name = NULL, Size = 0, Id = fr > >>> rotated file, > >>> + * rc = OK > >>> + */ > >>> + > >>> + if (int_rc == -1) { > >>> + /* No relevant file info found. Recovery fail */ > >>> + TRACE("%s: lgs_get_file_params_h Fail", __FUNCTION__); > >>> + rc_out = -1; > >>> + goto done; > >>> + } > >>> + > >>> + stream->logFileCurrent = par_out.curFileName; > >>> + stream->curFileSize = par_out.curFileSize; > >>> + stream->logRecordId = par_out.logRecordId; > >>> + > >>> + TRACE("Out: curFileSize = %u, logRecordId = %u, logFileCurrent > = > >>> %s", > >>> + stream->curFileSize, stream->logRecordId, > >>> stream->logFileCurrent.c_str()); > >>> + > >>> + if (stream->numOpeners != 0) { > >>> + TRACE("%s: numOpeners = %u Fail", __FUNCTION__, > >>> stream->numOpeners); > >>> + rc_out = -1; > >>> + goto done; > >>> + } > >>> + > >>> + int errno_save; > >>> + log_root_path = static_cast<const char > >>> *>(lgs_cfg_get(LGS_IMM_LOG_ROOT_DIRECTORY)); > >>> + *stream->p_fd = -1; > >>> + > >>> + if ((*stream->p_fd = log_file_open(log_root_path, stream, > >>> + stream->logFileCurrent, > >>> + &errno_save)) == -1) { > >>> + TRACE("%s - Could not open '%s' - %s", __FUNCTION__, > >>> + stream->logFileCurrent.c_str(), strerror(errno_save)); > >>> + } > >>> + > >>> + stream->numOpeners++; > >>> + > >>> +done: > >>> + // This memory is allocated in lgs_get_file_params_hdl() > >>> + if (par_out.curFileName != NULL) > >>> + free(par_out.curFileName); > >>> + > >>> + TRACE_LEAVE2("rc_out = %d", rc_out); > >>> + return rc_out; > >>> +} > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_recov.h > >>> b/osaf/services/saf/logsv/lgs/lgs_recov.h > >>> new file mode 100644 > >>> --- /dev/null > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_recov.h > >>> @@ -0,0 +1,37 @@ > >>> +/* -*- OpenSAF -*- > >>> + * > >>> + * (C) Copyright 2016 The OpenSAF Foundation > >>> + * > >>> + * This program is distributed in the hope that it will be > useful, > >>> but > >>> + * WITHOUT ANY WARRANTY; without even the implied warranty of > >>> MERCHANTABILITY > >>> + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program > are > >>> licensed > >>> + * under the GNU Lesser General Public License Version 2.1, > February > >>> 1999. > >>> + * The complete license can be accessed from the following > location: > >>> + * http://opensource.org/licenses/lgpl-license.php > >>> + * See the Copying file included with the OpenSAF distribution > for > >>> full > >>> + * licensing terms. > >>> + * > >>> + * Author(s): Ericsson AB > >>> + * > >>> + */ > >>> + > >>> +#ifndef LGS_STATE_H > >>> +#define LGS_STATE_H > >>> + > >>> +#include "lgs.h" > >>> + > >>> +int log_rtobj_list_add(char *dn_str); > >>> +int log_rtobj_list_no(); > >>> +int log_rtobj_list_find(char *stream_name); > >>> +int log_rtobj_list_getnamepos(); > >>> +char *log_rtobj_list_getname(int pos); > >>> +void log_rtobj_list_erase_one_pos(int pos); > >>> +void log_rtobj_list_free(); > >>> +int lgs_restore_one_app_stream( > >>> + char *stream_name, > >>> + uint32_t client_id, > >>> + log_stream_t **o_stream > >>> + ); > >>> +int log_stream_open_file_restore(log_stream_t *log_stream); > >>> + > >>> +#endif /* LGS_STATE_H */ > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_stream.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_stream.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_stream.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_stream.cc > >>> @@ -409,6 +409,33 @@ void log_stream_print(log_stream_t *stre > >>> TRACE_2(" filtered: %llu", stream->filtered); > >>> } > >>> > >>> +/** > >>> + * Free stream resources > >>> + * > >>> + * @param stream[in] > >>> + */ > >>> +void log_free_stream_resources(log_stream_t *stream) > >>> +{ > >>> + if (stream->streamId != 0) > >>> + lgs_stream_array_remove(stream->streamId); > >>> + > >>> + if (stream->pat_node.key_info != NULL) > >>> + log_stream_remove(stream->name); > >>> + > >>> + if (stream->logFileFormat != NULL) > >>> + free(stream->logFileFormat); > >>> + > >>> + delete stream; > >>> + stream = NULL; > >>> +} > >>> + > >>> +/** > >>> + * Remove a log stream including: > >>> + * - Runtime object if application stream and active > >>> + * - Remove stream resources (allocated memory) > >>> + * > >>> + * @param s[in] Pointer to the array of streams > >>> + */ > >>> void log_stream_delete(log_stream_t **s) > >>> { > >>> log_stream_t *stream; > >>> @@ -469,6 +496,12 @@ static void init_log_stream_fd(log_strea > >>> * Create a new stream object. If HA state active, create the > >>> * correspronding IMM runtime object. > >>> * > >>> + * Note: log_stream_new() is replaced by this function. > >>> + * The new function is doing the same as the old but the > >>> possibility > >>> + * to create a stream without creating a corresponding > runtime > >>> object > >>> + * is added. See creationFlag parameter > >>> + * > >>> + * Stream attributes[in]: > >>> * @param name > >>> * @param filename > >>> * @param pathname > >>> @@ -484,17 +517,21 @@ static void init_log_stream_fd(log_strea > >>> * > >>> * @return log_stream_t* > >>> */ > >>> -log_stream_t *log_stream_new(SaNameT *dn, > >>> - const std::string &filename, > >>> - const std::string &pathname, > >>> - SaUint64T maxLogFileSize, > >>> - SaUint32T fixedLogRecordSize, > >>> - SaLogFileFullActionT logFullAction, > >>> - SaUint32T maxFilesRotated, > >>> - const char *logFileFormat, > >>> - logStreamTypeT streamType, int stream_id, > >>> - SaBoolT twelveHourModeFlag, > >>> - uint32_t logRecordId) > >>> +log_stream_t *log_stream_new_1( > >>> + SaNameT *dn, > >>> + const std::string &filename, > >>> + const std::string &pathname, > >>> + SaUint64T maxLogFileSize, > >>> + SaUint32T fixedLogRecordSize, > >>> + SaLogFileFullActionT logFullAction, > >>> + SaUint32T maxFilesRotated, > >>> + const char *logFileFormat, > >>> + logStreamTypeT streamType, > >>> + int stream_id, > >>> + SaBoolT twelveHourModeFlag, > >>> + uint32_t logRecordId, > >>> + int creationFlag > >>> + ) > >>> { > >>> int rc; > >>> log_stream_t *stream = NULL; > >>> @@ -690,10 +727,10 @@ log_stream_t *log_stream_new(SaNameT *dn > >>> } > >>> > >>> /** > >>> - * Create a new stream object. Do not create an IMM runtime > object. > >>> + * Create a new default stream. Do not create an IMM runtime > object. > >>> * @param name > >>> * @param stream_id > >>> - * > >>> + * > >>> * @return log_stream_t* > >>> */ > >>> log_stream_t *log_stream_new_2(SaNameT *name, int stream_id) > >>> @@ -704,7 +741,7 @@ log_stream_t *log_stream_new_2(SaNameT * > >>> osafassert(name != NULL); > >>> TRACE_ENTER2("%s, l: %u", name->value, (unsigned > int)name->length); > >>> > >>> - stream = new (std::nothrow) (log_stream_t)(); > >>> + stream = new (std::nothrow) log_stream_t(); > >>> if (stream == NULL) { > >>> LOG_WA("calloc FAILED"); > >>> goto done; > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_stream.h > >>> b/osaf/services/saf/logsv/lgs/lgs_stream.h > >>> --- a/osaf/services/saf/logsv/lgs/lgs_stream.h > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_stream.h > >>> @@ -79,18 +79,21 @@ extern uint32_t log_stream_init(); > >>> extern void log_stream_delete(log_stream_t **s); > >>> > >>> #define STREAM_NEW -1 > >>> -extern log_stream_t *log_stream_new(SaNameT *name, > >>> - const std::string &filename, > >>> - const std::string &pathname, > >>> - SaUint64T maxLogFileSize, > >>> - SaUint32T fixedLogRecordSize, > >>> - SaLogFileFullActionT logFullAction, > >>> - SaUint32T maxFilesRotated, > >>> - const char *logFileFormat, > >>> - logStreamTypeT streamType, > >>> - int stream_id, > >>> - SaBoolT twelveHourModeFlag, > >>> - uint32_t logRecordId); > >>> +extern log_stream_t *log_stream_new_1( > >>> + SaNameT *name, > >>> + const std::string &filename, > >>> + const std::string &pathname, > >>> + SaUint64T maxLogFileSize, > >>> + SaUint32T fixedLogRecordSize, > >>> + SaLogFileFullActionT logFullAction, > >>> + SaUint32T maxFilesRotated, > >>> + const char *logFileFormat, > >>> + logStreamTypeT streamType, > >>> + int stream_id, > >>> + SaBoolT twelveHourModeFlag, > >>> + uint32_t logRecordId, > >>> + int creationFlag > >>> + ); > >>> > >>> extern log_stream_t *log_stream_new_2(SaNameT *name, int > stream_id); > >>> > >>> @@ -118,5 +121,6 @@ extern log_stream_t *log_stream_get_by_n > >>> extern log_stream_t *log_stream_getnext_by_name(const char > *name); > >>> extern void log_stream_print(log_stream_t *stream); > >>> extern log_stream_t *log_stream_get_by_id(uint32_t id); > >>> +void log_free_stream_resources(log_stream_t *stream); > >>> > >>> #endif > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_util.cc > >>> b/osaf/services/saf/logsv/lgs/lgs_util.cc > >>> --- a/osaf/services/saf/logsv/lgs/lgs_util.cc > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_util.cc > >>> @@ -30,12 +30,13 @@ > >>> > >>> #include <stdlib.h> > >>> #include <grp.h> > >>> +#include <osaf_time.h> > >>> > >>> #include "immutil.h" > >>> #include "lgs.h" > >>> #include "lgs_file.h" > >>> #include "lgs_filehdl.h" > >>> -#include "osaf_time.h" > >>> +#include "osaf_timerfd.h" > >>> > >>> #define ALARM_STREAM_ENV_PREFIX "ALARM" > >>> #define NOTIFICATION_STREAM_ENV_PREFIX "NOTIFICATION" > >>> @@ -239,14 +240,14 @@ SaTimeT lgs_get_SaTime() > >>> * @param root_path[in] > >>> * @param rel_path[in] > >>> * @param old_name[in] > >>> - * @param time_stamp[in] > >>> + * @param time_stamp[in] > >>> * Can be set to NULL but then new_name must be the > complete > >>> new name > >>> * including time stamps but without suffix > >>> * @param suffix[in] > >>> - * @param new_name[in/out] > >>> - * Pointer to char string of NAME_MAX size > >>> + * @param new_name[in/out] > >>> + * Pointer to char string > >>> * Filename of renamed file. Can be set to NULL > >>> - * > >>> + * > >>> * @return -1 if error > >>> */ > >>> int lgs_file_rename_h( > >>> @@ -638,6 +639,45 @@ done: > >>> } > >>> > >>> /** > >>> + * Initiate a timer: > >>> + * Creates a timeout timer, set timeout time and starts the > timer > >>> + * returns a file descriptor to the timer that can be used with > >>> poll() > >>> + * See also osaf_timerfd.h > >>> + * > >>> + * @param timeout_s[in] Timeout time in seconds > >>> + * @return fd File descriptor referring to the created timer > >>> + */ > >>> +int lgs_init_timer(time_t timeout_s) > >>> +{ > >>> + int fd; > >>> + struct itimerspec lgs_cltimer; > >>> + > >>> + fd = osaf_timerfd_create(CLOCK_MONOTONIC, 0); > >>> + > >>> + /* Set timeout time. Do not use as interval timer */ > >>> + lgs_cltimer.it_interval.tv_sec = 0; > >>> + lgs_cltimer.it_interval.tv_nsec = 0; > >>> + /* Set timeout in seconds */ > >>> + lgs_cltimer.it_value.tv_sec = timeout_s; > >>> + lgs_cltimer.it_value.tv_nsec = 0; > >>> + > >>> + osaf_timerfd_settime(fd, 0, &lgs_cltimer, NULL); > >>> + > >>> + return fd; > >>> +} > >>> + > >>> +/** > >>> + * Close a timer created with lgs_init_timer() > >>> + * See also osaf_timerfd.h > >>> + * > >>> + * @param ufd[in] > >>> + */ > >>> +void lgs_close_timer(int ufd) > >>> +{ > >>> + osaf_timerfd_close(ufd); > >>> +} > >>> + > >>> +/** > >>> * Validate if string contains special characters or not > >>> * > >>> * @param: str [in] input string for checking > >>> diff --git a/osaf/services/saf/logsv/lgs/lgs_util.h > >>> b/osaf/services/saf/logsv/lgs/lgs_util.h > >>> --- a/osaf/services/saf/logsv/lgs/lgs_util.h > >>> +++ b/osaf/services/saf/logsv/lgs/lgs_util.h > >>> @@ -77,4 +77,8 @@ bool lgs_is_valid_pathlength(const std:: > >>> const std::string &fileName, > >>> const std::string &rootPath = ""); > >>> > >>> +/* Timer functions */ > >>> +int lgs_init_timer(time_t timeout_s); > >>> +void lgs_close_timer(int ufd); > >>> + > >>> #endif /* ifndef __LGS_UTIL_H */ ------------------------------------------------------------------------------ Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now! http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140 _______________________________________________ Opensaf-devel mailing list Opensaf-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/opensaf-devel