Hi Lennart,

I have just pushed a new version of this ticket based on Vu Minh Nguyen
comments.
It would be nice if you take your time to review the new one.
But I still consider your comments of the old version for adapting into the
new one if it is necessary.
Thank you in advance!

Best regards,
Danh Vo

-----Original Message-----
From: Lennart Lund <lennart.l...@ericsson.com> 
Sent: Monday, July 30, 2018 6:36 PM
To: Danh Cong Vo <danh.c...@dektech.com.au>; Hans Nordebäck
<hans.nordeb...@ericsson.com>; Gary Lee <gary....@dektech.com.au>; Vu Minh
Nguyen <vu.m.ngu...@dektech.com.au>
Cc: opensaf-devel@lists.sourceforge.net; Danh Cong Vo
<danh.c...@dektech.com.au>; Lennart Lund <lennart.l...@ericsson.com>
Subject: RE: [PATCH 1/1] imm: syslog recent fevs evts when immnd down
[#2898]

Hi Danh Vo

Most of my comments can be found in the attached .diff file. This diff can
be applied on top of the review branch

A summary of my comments:
  Do not add a feature as code distributed in several files and functions
not related to the feature. Keep the feature in in one file and create an
API.
  Do not add a feature to an already existing file created for some other
purpose even if this new feature is used there.
  Use C++. In this case C++ vector of strings can be used instead of a char
array. In this case C functions are needed as a part of the API but that is
not a problem to have in a file compiled with a C++ compiler
  Do not add and handle global variables inline in code that has a
completely different purpose. Instead call a function with a descriptive
name
  Give all new functions descriptive names. By descriptive name means a name
that tells a reader what the function does not what data it handles. See
example in the attached comments
  In the file that contains all the new log handling, write a comment
describing the purpose of the new feature

Thanks
Lennart

> -----Original Message-----
> From: Danh Vo <danh.c...@dektech.com.au>
> Sent: den 27 juli 2018 06:46
> To: Hans Nordebäck <hans.nordeb...@ericsson.com>; Lennart Lund 
> <lennart.l...@ericsson.com>; Gary Lee <gary....@dektech.com.au>; Vu 
> Minh Nguyen <vu.m.ngu...@dektech.com.au>
> Cc: opensaf-devel@lists.sourceforge.net; Danh Cong Vo 
> <danh.c...@dektech.com.au>
> Subject: [PATCH 1/1] imm: syslog recent fevs evts when immnd down 
> [#2898]
> 
> An enhancement for IMM to print out 5 last FEVS events into syslog 
> when IMMND down for any reason.
> ---
>  src/imm/common/immsv_evt.c |   2 +-
>  src/imm/common/immsv_evt.h |   2 +
>  src/imm/immnd/ImmModel.cc  | 285
> +++++++++++++++++++++++++++++++++++++++++++++
>  src/imm/immnd/ImmModel.h   |   1 +
>  src/imm/immnd/immnd_evt.c  |  34 +++++-
>  src/imm/immnd/immnd_init.h |   2 +
>  6 files changed, 322 insertions(+), 4 deletions(-)
> 
> diff --git a/src/imm/common/immsv_evt.c b/src/imm/common/immsv_evt.c 
> index b0e36d3..03a7f81 100644
> --- a/src/imm/common/immsv_evt.c
> +++ b/src/imm/common/immsv_evt.c
> @@ -189,7 +189,7 @@ static const char *immnd_evt_names[] = {
>      "IMMND_EVT_D2ND_IMPLDELETE",
>      "undefined (high)"};
> 
> -static const char *immsv_get_immnd_evt_name(unsigned int id)
> +const char *immsv_get_immnd_evt_name(unsigned int id)
>  {
>       if (id < IMMND_EVT_MAX)
>               return immnd_evt_names[id];
> diff --git a/src/imm/common/immsv_evt.h b/src/imm/common/immsv_evt.h 
> index 3e96c98..a1dabc5 100644
> --- a/src/imm/common/immsv_evt.h
> +++ b/src/imm/common/immsv_evt.h
> @@ -689,6 +689,8 @@ void
> immsv_free_attrdefs_list(IMMSV_ATTR_DEF_LIST *adp);  void 
> immsv_evt_free_name_list(IMMSV_OBJ_NAME_LIST *p);  void 
> immsv_evt_free_ccbOutcomeList(IMMSV_CCB_OUTCOME_LIST *o);
> 
> +const char *immsv_get_immnd_evt_name(unsigned int id);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/src/imm/immnd/ImmModel.cc b/src/imm/immnd/ImmModel.cc 
> index 21f48ab..80fdf84 100644
> --- a/src/imm/immnd/ImmModel.cc
> +++ b/src/imm/immnd/ImmModel.cc
> @@ -35,6 +35,7 @@
>  #define PRT_HIGH_THRESHOLD 4   /* See ImmModel::immNotPbeWritable
> */
>  #define CCB_CRIT_THRESHOLD 8   /* See ImmModel::immNotPbeWritable
> */
>  #define SEARCH_TIMEOUT_SEC 600 /* Search timeout */
> +#define MAX_LENGTH_EVT_DATA 256
> 
>  struct ContinuationInfo2 {
>    ContinuationInfo2()
> @@ -20171,3 +20172,287 @@ void ImmModel::isolateThisNode(unsigned int 
> thisNode, bool isAtCoord) {
>       immnd_proc_imma_discard_connection()
>     */
>  }
> +
> +
> +/*********************************************************
> *************
> + * An enhancement from ticket #2898 to get more info from events
> +
> **********************************************************
> ************/
> +static std::string getInfoForObjCreate(const ImmsvOmCcbObjectCreate
> *req)
> +{
> +  std::string additionalInfo = "";
> +  size_t sz = strnlen(req->className.buf, 
> +(size_t)req->className.size);
> +  std::string className((const char*)req->className.buf, sz);
> +
> +  if (className.empty()) {
> +    additionalInfo.append("No info");  } else {
> +    additionalInfo.append("Create obj of class ").append(className);  
> + }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForObjModify(const ImmsvOmCcbObjectModify*
> req)
> +{
> +  std::string additionalInfo = "";
> +  size_t sz = strnlen(req->objectName.buf, 
> +(size_t)req->objectName.size);
> +  std::string objName((const char*)req->objectName.buf, sz);
> +
> +  if (objName.empty()) {
> +    additionalInfo.append("No info");  } else {
> +    additionalInfo.append(objName);
> +  }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForObjDelete(const ImmsvOmCcbObjectDelete*
> req)
> +{
> +  std::string additionalInfo = "";
> +  size_t sz = strnlen(req->objectName.buf, 
> +(size_t)req->objectName.size);
> +  std::string objName((const char*)req->objectName.buf, sz);
> +
> +  if (objName.empty()) {
> +    additionalInfo.append("No info");  } else {
> +    additionalInfo.append(objName);
> +  }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForObjSync(const ImmsvOmObjectSync *req) {
> +  std::string additionalInfo = "";
> +  size_t sz = strnlen(req->objectName.buf, 
> +(size_t)req->objectName.size);
> +  std::string objName((const char*)req->objectName.buf, sz);
> +
> +  if (objName.empty()) {
> +    additionalInfo.append("No info");  } else {
> +    additionalInfo.append(objName);
> +  }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForAdmOp(const
> ImmsvOmAdminOperationInvoke* req)
> +{
> +  std::string additionalInfo = "";
> +  size_t sz = strnlen(req->objectName.buf, 
> +(size_t)req->objectName.size);
> +  std::string objName((const char*)req->objectName.buf, sz);
> +  std::string opId = std::to_string(req->operationId);
> +
> +  if (objName.empty()) {
> +    additionalInfo.append("No info");  } else {
> +    additionalInfo.append(objName).append("(opId:").append(opId);
> +  }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForClassDesc(const ImmsvOmClassDescr *req) 
> +{
> +  std::string additionalInfo = "";
> +  size_t sz = strnlen((char*)req->className.buf, (size_t)req-
> >className.size);
> +  std::string className((const char*)req->className.buf, sz);
> +
> +  if (className.empty()) {
> +    additionalInfo.append("No info");  } else {
> +    additionalInfo.append(className);  }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForAdmInit(const immsv_d2nd_adminit *req) {
> +  std::string additionalInfo = "";
> +  std::string ownerId = std::to_string(req->globalOwnerId);
> +  std::string ownerName = osaf_extended_name_borrow(&(req-
> >i.adminOwnerName));
> +
> +  if (ownerId.empty()) {
> +    additionalInfo.append("No info");  } else {
> +
> additionalInfo.append(ownerId).append("(").append(ownerName).append(
> ")");
> +  }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForImplSet(const ImmsvOiImplSetReq *req,
> +                                     bool isSet = false) {
> +  std::string additionalInfo = "";
> +  size_t sz = strnlen((const char*)req->impl_name.buf, req-
> >impl_name.size);
> +  std::string clsOrObjName((const char*)req->impl_name.buf, sz);
> +
> +  if (clsOrObjName.empty()) {
> +    additionalInfo.append("No info");  } else {
> +    if (isSet) {
> +      additionalInfo.append("Set impl ")
> +          .append(std::to_string(req->impl_id))
> +          .append(" to ").append(clsOrObjName);
> +    } else {
> +      additionalInfo.append("Release impl ")
> +          .append(std::to_string(req->impl_id))
> +          .append(" out of ").append(clsOrObjName);
> +    }
> +  }
> +
> +  return additionalInfo;
> +}
> +
> +static std::string getInfoForSafeReadLock(const ImmsvOmSearchInit 
> +*req) {
> +  std::string additionalInfo = "";
> +  std::string ccbId = std::to_string(req->ccbId);
> +  size_t sz = strnlen(req->rootName.buf, (size_t)req->rootName.size);
> +  std::string objName((const char*)req->rootName.buf, sz);
> +
> +  additionalInfo.append("CCB ").append(ccbId).append(" lock
> ").append(objName);
> +  return additionalInfo;
> +}
> +
> +void immModel_getEvtData(IMMND_CB *cb, IMMND_EVT *evt, char*
> evt_data)
> +{
> +  std::string additionalInfo = "";
> +  std::string eventName(immsv_get_immnd_evt_name(evt->type));
> +
> +  switch (evt->type) {
> +    case IMMND_EVT_A2ND_OBJ_CREATE:
> +    case IMMND_EVT_A2ND_OBJ_CREATE_2:
> +    case IMMND_EVT_A2ND_OI_OBJ_CREATE:
> +    case IMMND_EVT_A2ND_OI_OBJ_CREATE_2:
> +      additionalInfo = getInfoForObjCreate(&(evt->info.objCreate));
> +      break;
> +
> +    case IMMND_EVT_A2ND_OBJ_MODIFY:
> +    case IMMND_EVT_A2ND_OI_OBJ_MODIFY:
> +      additionalInfo = getInfoForObjModify(&(evt->info.objModify));
> +      break;
> +
> +    case IMMND_EVT_A2ND_OBJ_DELETE:
> +    case IMMND_EVT_A2ND_OI_OBJ_DELETE:
> +      additionalInfo = getInfoForObjDelete(&(evt->info.objDelete));
> +      break;
> +
> +    case IMMND_EVT_A2ND_OBJ_SYNC:
> +    case IMMND_EVT_A2ND_OBJ_SYNC_2:
> +      additionalInfo = getInfoForObjSync(&(evt->info.obj_sync));
> +      break;
> +
> +    case IMMND_EVT_A2ND_IMM_ADMOP:
> +    case IMMND_EVT_A2ND_IMM_ADMOP_ASYNC:
> +      additionalInfo = getInfoForAdmOp(&(evt->info.admOpReq));
> +      break;
> +
> +    case IMMND_EVT_A2ND_CLASS_CREATE:
> +    case IMMND_EVT_A2ND_CLASS_DELETE:
> +      additionalInfo = getInfoForClassDesc(&(evt->info.classDescr));
> +      break;
> +
> +    case IMMND_EVT_D2ND_DISCARD_IMPL:
> +    case IMMND_EVT_D2ND_IMPLSET_RSP:
> +    case IMMND_EVT_D2ND_IMPLSET_RSP_2:
> +    case IMMND_EVT_A2ND_OI_IMPL_CLR:
> +      additionalInfo.append("impl_id:")
> +          .append(std::to_string(evt->info.implSet.impl_id));
> +      break;
> +
> +    case IMMND_EVT_D2ND_ADMINIT:
> +      additionalInfo = getInfoForAdmInit(&(evt->info.adminitGlobal));
> +      break;
> +
> +    case IMMND_EVT_D2ND_CCBINIT:
> +      additionalInfo.append("ccbId:")
> +          .append(std::to_string(evt->info.ccbinitGlobal.globalCcbId));
> +      break;
> +
> +    case IMMND_EVT_A2ND_AUG_ADMO:
> +      additionalInfo.append("Add admo_id ")
> +          .append(std::to_string(evt->info.objDelete.adminOwnerId))
> +          .append(" to CCB ")
> +          .append(std::to_string(evt->info.objDelete.ccbId));
> +      break;
> +
> +    case IMMND_EVT_A2ND_OI_CL_IMPL_SET:
> +    case IMMND_EVT_A2ND_OI_OBJ_IMPL_SET:
> +      additionalInfo = getInfoForImplSet(&(evt->info.implSet), true);
> +      break;
> +
> +    case IMMND_EVT_A2ND_OI_CL_IMPL_REL:
> +    case IMMND_EVT_A2ND_OI_OBJ_IMPL_REL:
> +      additionalInfo = getInfoForImplSet(&(evt->info.implSet));
> +      break;
> +
> +    case IMMND_EVT_A2ND_ADMO_FINALIZE:
> +    case IMMND_EVT_D2ND_ADMO_HARD_FINALIZE:
> +    case IMMND_EVT_A2ND_ADMO_SET:
> +    case IMMND_EVT_A2ND_ADMO_RELEASE:
> +    case IMMND_EVT_A2ND_ADMO_CLEAR:
> +      additionalInfo.append("admo_id:")
> +          .append(std::to_string(evt->info.admFinReq.adm_owner_id));
> +      break;
> +
> +    case IMMND_EVT_A2ND_OI_CCB_AUG_INIT:
> +    case IMMND_EVT_A2ND_CCB_COMPLETED_RSP:
> +    case IMMND_EVT_A2ND_CCB_COMPLETED_RSP_2:
> +    case IMMND_EVT_A2ND_CCB_OBJ_CREATE_RSP:
> +    case IMMND_EVT_A2ND_CCB_OBJ_CREATE_RSP_2:
> +    case IMMND_EVT_A2ND_CCB_OBJ_MODIFY_RSP:
> +    case IMMND_EVT_A2ND_CCB_OBJ_MODIFY_RSP_2:
> +    case IMMND_EVT_A2ND_CCB_OBJ_DELETE_RSP:
> +    case IMMND_EVT_A2ND_CCB_OBJ_DELETE_RSP_2:
> +      additionalInfo.append("ccbId:")
> +          .append(std::to_string(evt->info.ccbUpcallRsp.ccbId));
> +      break;
> +
> +    case IMMND_EVT_A2ND_CCB_APPLY:
> +    case IMMND_EVT_A2ND_CCB_VALIDATE:
> +    case IMMND_EVT_A2ND_CCB_FINALIZE:
> +    case IMMND_EVT_D2ND_ABORT_CCB:
> +      additionalInfo.append("ccbId:")
> +          .append(std::to_string(evt->info.ccbId));
> +      break;
> +
> +    case IMMND_EVT_A2ND_PBE_ADMOP_RSP:
> +      additionalInfo.append("admop result:")
> +          .append(std::to_string(evt->info.admOpRsp.result));
> +      break;
> +
> +    case IMMND_EVT_D2ND_SYNC_FEVS_BASE:
> +      additionalInfo.append("sync fevs base:")
> +          .append(std::to_string(evt->info.syncFevsBase));
> +      break;
> +
> +    case IMMND_EVT_A2ND_OBJ_SAFE_READ:
> +      additionalInfo = getInfoForSafeReadLock(&(evt->info.searchInit));
> +      break;
> +
> +    case IMMND_EVT_D2ND_IMPLDELETE:
> +      additionalInfo.append("Delete ")
> +          .append(std::to_string(evt->info.impl_delete.size))
> +          .append(" implementer(s).");
> +      break;
> +
> +    case IMMND_EVT_D2ND_DISCARD_NODE:
> +      additionalInfo.append("nodeId: ")
> +          .append(std::to_string(evt->info.ctrl.nodeId));
> +      break;
> +    default:
> +      additionalInfo = "";
> +      break;
> +  }
> +
> +  if (additionalInfo.empty()) {
> +    snprintf(evt_data, MAX_LENGTH_EVT_DATA, "%s", eventName.c_str());
> +  } else {
> +    snprintf(evt_data, MAX_LENGTH_EVT_DATA,
> +        "%s -> %s", eventName.c_str(), additionalInfo.c_str());
> +  }
> +}
> diff --git a/src/imm/immnd/ImmModel.h b/src/imm/immnd/ImmModel.h index 
> c8b0f4e..23b3692 100644
> --- a/src/imm/immnd/ImmModel.h
> +++ b/src/imm/immnd/ImmModel.h
> @@ -60,6 +60,7 @@ struct ImmOiRtObjectCreate;  struct 
> immsv_oi_ccb_upcall_rsp;  struct immsv_attr_values_list;  struct 
> immsv_attr_mods_list;
> +struct immsv_d2nd_adminit;
> 
>  struct ImmsvOmRspSearchNext;
> 
> diff --git a/src/imm/immnd/immnd_evt.c b/src/imm/immnd/immnd_evt.c 
> index 730c490..db94036 100644
> --- a/src/imm/immnd/immnd_evt.c
> +++ b/src/imm/immnd/immnd_evt.c
> @@ -39,6 +39,20 @@
>  #define IMMND_SEARCH_BUNDLE_SIZE ((MDS_DIRECT_BUF_MAXSIZE /
> 100) * 90)
>  #define IMMND_MAX_SEARCH_RESULT (IMMND_SEARCH_BUNDLE_SIZE /
> 300)
> 
> +#define MAX_INDEX_FEVS_MSG 5
> +#define MAX_LENGTH_FEVS_MSG 256
> +
> +static char
> latest_fevs[MAX_INDEX_FEVS_MSG][MAX_LENGTH_FEVS_MSG] = {
> +    "(none)", "(none)", "(none)", "(none)", "(none)"
> +};
> +
> +static void syslog_recent_fevs()
> +{
> +  for (int i = 0; i < MAX_INDEX_FEVS_MSG; i++) {
> +    LOG_NO("Recent fevs: %s", latest_fevs[i]);
> +  }
> +}
> +
>  static SaAisErrorT immnd_fevs_local_checks(IMMND_CB *cb, IMMSV_FEVS 
> *fevsReq,
>                                          const IMMSV_SEND_INFO *sinfo);
static uint32_t 
> immnd_evt_proc_cb_dump(IMMND_CB *cb); @@ -9601,7 +9615,8 @@ static 
> uint32_t immnd_restricted_ok(IMMND_CB *cb, uint32_t id)  static 
> SaAisErrorT  immnd_evt_proc_fevs_dispatch(IMMND_CB *cb, 
> IMMSV_OCTET_STRING *msg,
>                            bool originatedAtThisNd, SaImmHandleT clnt_hdl,
> -                          MDS_DEST reply_dest, SaUint64T msgNo)
> +                          MDS_DEST reply_dest, SaUint64T msgNo,
> +                          char* evt_data)
>  {
>       SaAisErrorT error = SA_AIS_OK;
>       IMMSV_EVT frwrd_evt;
> @@ -9665,7 +9680,7 @@ immnd_evt_proc_fevs_dispatch(IMMND_CB *cb, 
> IMMSV_OCTET_STRING *msg,
> 
>       /*Dispatch the unpacked FEVS message */
>       immsv_msg_trace_rec(frwrd_evt.sinfo.dest, &frwrd_evt);
> -
> +     immModel_getEvtData(immnd_cb, &frwrd_evt.info.immnd,
> evt_data);
>       switch (frwrd_evt.info.immnd.type) {
>       case IMMND_EVT_A2ND_OBJ_CREATE:
>       case IMMND_EVT_A2ND_OBJ_CREATE_2:
> @@ -10735,7 +10750,11 @@ static uint32_t 
> immnd_evt_proc_fevs_rcv(IMMND_CB *cb, IMMND_EVT *evt,
>       bool isObjSync = (evt->type ==
> IMMND_EVT_D2ND_GLOB_FEVS_REQ_2)
>                            ? evt->info.fevsReq.isObjSync
>                            : false;
> +     static int index = 0;
> +     char evt_data[MAX_LENGTH_FEVS_MSG];
> +
>       TRACE_ENTER();
> +     memset(evt_data, 0, sizeof(evt_data));
> 
>       if (cb->highestProcessed >= msgNo) {
>               /*We have already received this message, discard it. */ @@
-10784,6 
> +10803,7 @@ static uint32_t immnd_evt_proc_fevs_rcv(IMMND_CB *cb, 
> IMMND_EVT *evt,
>                       LOG_ER(
>                           "MESSAGE:%llu OUT OF ORDER my highest
processed:%llu - 
> exiting",
>                           msgNo, cb->highestProcessed);
> +                     syslog_recent_fevs();
>                       immnd_ackToNid(NCSCC_RC_FAILURE);
>                       exit(1);
>               } else if (cb
> @@ -10792,6 +10812,7 @@ static uint32_t 
> immnd_evt_proc_fevs_rcv(IMMND_CB *cb, IMMND_EVT *evt,
>                       LOG_ER(
>                           "Sync MESSAGE:%llu OUT OF ORDER my highest
processed:%llu - 
> exiting",
>                           msgNo, cb->highestProcessed);
> +                     syslog_recent_fevs();
>                       immnd_ackToNid(NCSCC_RC_FAILURE);
>                       exit(1);
>               } else if (cb->mState < IMM_SERVER_LOADING_PENDING) { @@
-10816,7 
> +10837,8 @@ static uint32_t immnd_evt_proc_fevs_rcv(IMMND_CB *cb, 
> IMMND_EVT *evt,
>               TRACE("Coord discards object sync message");
>       } else {
>               err = immnd_evt_proc_fevs_dispatch(cb, msg,
originatedAtThisNd,
> -                                                clnt_hdl, reply_dest,
> msgNo);
> +                                                clnt_hdl, reply_dest,
> +                                                msgNo, evt_data);
>       }
> 
>       if (err != SA_AIS_OK) {
> @@ -10826,7 +10848,11 @@ static uint32_t 
> immnd_evt_proc_fevs_rcv(IMMND_CB *cb, IMMND_EVT *evt,
>                       LOG_ER("PROBLEM %u WITH msg no:%llu", err, msgNo);
>               }
>       }
> +     memset(latest_fevs[index], 0, MAX_LENGTH_FEVS_MSG);
> +     snprintf(latest_fevs[index], MAX_LENGTH_FEVS_MSG,
> +         "<%llu>[%s]", msgNo, evt_data);
> 
> +     if (++index > MAX_INDEX_FEVS_MSG - 1) index = 0;
>  done:
>       cb->highestProcessed++;
>       dequeue_outgoing(cb);
> @@ -10912,6 +10938,8 @@ static void
> immnd_evt_proc_discard_node(IMMND_CB *cb, IMMND_EVT *evt,
>                      cb->node_id);
>               exit(1);
>       }
> +
> +     syslog_recent_fevs();
>       LOG_NO("Global discard node received for nodeId:%x pid:%u",
>              evt->info.ctrl.nodeId, evt->info.ctrl.ndExecPid);
>       /* We should remember the nodeId/pid pair to avoid a redundant 
> message diff --git a/src/imm/immnd/immnd_init.h 
> b/src/imm/immnd/immnd_init.h index 1d0126e..b626df9 100644
> --- a/src/imm/immnd/immnd_init.h
> +++ b/src/imm/immnd/immnd_init.h
> @@ -485,6 +485,8 @@ void immModel_sendSyncAbortAt(IMMND_CB *cb, struct 
> timespec time);
> 
>  void immModel_getSyncAbortRsp(IMMND_CB *cb);
> 
> +void immModel_getEvtData(IMMND_CB *cb, IMMND_EVT *evt, char*
> evt_data);
> +
>  bool is_regular_name(const char* name, bool strict);  bool 
> is_valid_schema_name(const char* name);
> 
> --
> 2.7.4



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

Reply via email to