Issue with processing nested list without having key

Hi,
I have a nested list without key as below:

I have created a callpoint for active-alarms.
Currently I am facing two issue:
1.get element is not getting called
2.nested list which is affected-objects also not getting called.
Both doesn’t have key.
I have followed 5-c_stats example. for the same.
If you can review the below code and find the issue then it will really be helpful.

     int fm_status_get_elem(struct confd_trans_ctx *tctx, confd_hkeypath_t * kp)
     {

    confd_value_t v;

     struct confd_datetime eventTime;

    switch( CONFD_GET_XMLTAG(&kp->v[0][0]))
    {

        case o_ran_fm_fault_id:
            {

		alwaysLog("o_ran_fm_fault_id");
                CONFD_SET_UINT16(&v,100);
                confd_data_reply_value(tctx, &v);
            }
            break;
        case o_ran_fm_fault_source:
            {

		alwaysLog("o_ran_fm_fault_source");
                CONFD_SET_STR(&v,"SOURCE");
                confd_data_reply_value(tctx, &v);
            }
            break;
        case o_ran_fm_fault_severity:
            {

		alwaysLog("o_ran_fm_fault_severity");
                CONFD_SET_ENUM_VALUE(&v,1);
                confd_data_reply_value(tctx, &v);
            }
            break;
        case o_ran_fm_is_cleared:
            {

				alwaysLog("o_ran_fm_is_cleared");
                CONFD_SET_BOOL(&v,1);
                confd_data_reply_value(tctx, &v);
            }
            break;
        case o_ran_fm_fault_text:
            {

				alwaysLog("o_ran_fm_fault_text");
                CONFD_SET_STR(&v,"TEXT");
                confd_data_reply_value(tctx, &v);
            }
            break;
        case o_ran_fm_event_time:
            {
				alwaysLog("o_ran_fm_event_time");
				getdatetime(&eventTime);
				CONFD_SET_DATETIME(&v,eventTime);
            }
            break;
    }

    return CONFD_OK;

}

      Int fm_status_get_next(struct confd_trans_ctx *tctx,
        confd_hkeypath_t * kp, long next)
     {
           confd_value_t v[2];
          int pos;
                  alwaysLog("GET NEXT PRINT");

    switch (CONFD_GET_XMLTAG(&kp->v[0][0])) {
        case o_ran_fm_active_alarms:
            alwaysLog("ACTIVE");
            if (next == -1) {  /* first call */
                pos = 0;
            } else {
                pos = (int)next;
            }
            if (pos >= 6) { /* We have reached the end of the list*/
                confd_data_reply_next_key(tctx, NULL, -1, -1);
                return CONFD_OK;
            }
        CONFD_SET_UINT16(&v[0],11);
            confd_data_reply_next_key(tctx, v,1, (long)(pos + 1));
            break;
        case o_ran_fm_affected_objects:
            alwaysLog("OBJECT");
            if (next == -1) {  /* first call */
                pos = 0;
                confd_data_reply_next_key(tctx, v, 1, 1);
            } else {
                //      pos = (int)next;
                confd_data_reply_next_key(tctx, NULL, -1, -1);
            }
 
            break;
       default:
            return CONFD_ERR;

    }

    return CONFD_OK;

}

fm_status_get_next callback to get_next and fm_status_get_elem callback to get_elem
and get_next and passed to confd_register_trans_cb(dctx, &tcb) through tcb object.

Currently, only once the outer list i.e active-alarms of fm_status_get_next is printing.

Regards,
Biswajit

By list without key, do you mean keyless lists? (If so see 8-c_stats_no_key) Example.
get_next and get_elem should be passed to confd_register_data_cb, not to confd_register_trans_cb as you write (you can see it in the example).

Hi,
Sorry it was a Typo error.
Yes,both the outer list as well as inner list doesn’t have a key.

    #define CALLPOINT_DECL(mib, cp_id, get_elem_cb, get_next_cb,get_object_cb)    \
                {.callpoint = CALLPOINT_ID(mib, cp_id),                        \
                                             .get_elem = get_elem_cb,          \
                                             .get_next = get_next_cb,          \
                                             .get_object = get_object_cb }

  
     void fm_setup(struct confd_daemon_ctx *dctx,    int rsock)
    {
             struct confd_data_cbs dcb[] = {
        CALLPOINT_DECL(o_ran_fm,cb_op_fm,fm_status_get_elem,
                fm_status_get_next,NULL)
                };

      int i;

    /* Register data callbacks */
    for (i = 0; i < sizeof(dcb) / sizeof(struct confd_data_cbs); i++) {
        if (confd_register_data_cb(dctx, &dcb[i]) != CONFD_OK)
            alwaysLog("Failed to register callpoint \"%s\"", dcb[i].callpoint);
       }

    } 

As i said earlier, only once the get_next callback i.e fm_status_get_next is getting called once.
Not sure why get_elem callback is not getting called.

Regards,
Biswajit

See guidelines in User Guide - section Operational data lists without keys

Your code returns same key value repeatedly (11) for sub-sequent get-next() calls of parent key-less list. This conflicts with requirement that key must allow to retrieve specific list entry values identified by pseudo-key…

Your copy&paste does not show exists_optional() implementation - U.G. states that this is needed for servicing key-less lists for list entry existence checks…

Make sure you enable developer log in ConfD, and trace log in confd-lib in your application.
It usually gives more hints on when specifically something goes wrong or give better traceability -> lets us see sequence of operations that will help identify what is wrong…

Hi,
Thanks for figuring out the mistake. But I am not getting what I am supposed to add in the .exists_optional call back.

   struct aentry *find_ae(confd_hkeypath_t *keypath, struct arpdata *dp)
{
 return (struct aentry *)CONFD_GET_INT64(&keypath->v[1][0]);
}

static int exists(struct confd_trans_ctx *tctx,
              confd_hkeypath_t *keypath)
{
struct aentry *ae = find_ae(keypath, tctx->t_opaque);
if (ae == NULL) {
    confd_data_reply_not_found(tctx);
} else {
    confd_data_reply_found(tctx);
}
return CONFD_OK;
}

Can you please help me in modifying the same.

Regards,
Biswajit

exists_optional() is used to answer the ConfD’s question: “Does element with this keypath exist?”.

  1. Input argument of the callback asks about some element existence (in this case it would be something like /active-alarm-list{11}.

  2. Your code must decide whether such oper. data exists or not.
    (confd_data_reply_found() or confd_data_reply_not_found() )
    This is up to your specific code conditions -> how/where you have oper data etc.

The example code you pasted is from ConfD example, and it looks into internal memory representation of oper. data that example data provider implement.

In this case, you need to replace this code and get needed info from your source of oper. data…

If you tell ConfD that some data exists, it will continue with asking for nested children/leaves etc., if you tell it it does not, it will not continue with callbacks invocation for nested elements…

Hi ,
Thanks a lot for your valuable input. I have added call back to exists_optional and
Now I am able to fetch all the values from confd server through netconf-console(netconf-console --host=10.10.11.101 -port=2023 --get -x /active-alarm-list).

But still, I am facing few issues like :

  1. server application is getting crashed when I the releasing memory call back of finish_data ie in fm_finish_data function.
free(): invalid pointer
Aborted

The developer log shows :

  1. 20-Aug-2020::19:36:53.830 FXT01200014 confd[3806]: devel-c close_trans/"Operational" error {external, ""}

  2. The callback to exists_optional is not getting called.

Not sure if Issues 1 & 2 are interlinked or not.

Please find the complete updated code below :

     typedef struct alarmstatus
      {
        int alarmid;
       }activeAlarm;

    static int fm_init_data(struct confd_trans_ctx *tctx )
    {
    alwaysLog("INSIDE fm_init_data");
    activeAlarm *dp ;
    if ((dp = malloc(2 *sizeof(activeAlarm))) == NULL)
        return CONFD_ERR;
    memset(dp, 0, 2 * sizeof(activeAlarm));
    tctx->t_opaque =dp;
    return CONFD_OK;
    }


    static int fm_status_get_next(struct confd_trans_ctx *tctx,
        confd_hkeypath_t * kp, long next)
    {
    confd_value_t v[2];
    int pos=0;
    alwaysLog("GET NEXT PRINT");
    activeAlarm *dp=tctx->t_opaque;
    if (next == -1) {  /* first call */
        pos = 0;
        alwaysLog("*****FIRST CALL*****");
    } else {
        pos = (int)next;
        alwaysLog("Inside else");
    }
    if (pos >= 2) { /* We have reached the end of the list*/
        alwaysLog("******POS:%d",pos);
        confd_data_reply_next_key(tctx, NULL, -1, -1);
        return CONFD_OK;
    }
        CONFD_SET_INT64(&v[0],(int64_t )dp);
        confd_data_reply_next_key(tctx, &v[0],1, (long)(pos +1));
        return CONFD_OK;


      }
     int fm_status_get_elem(struct confd_trans_ctx *tctx, confd_hkeypath_t * kp)
     {
       confd_value_t v;
     struct confd_datetime eventTime;
     alwaysLog("Inside fm_status_get_elem");
      activeAlarm *ae = find_ae(kp, tctx->t_opaque);
    if (ae == NULL ) {
        alwaysLog("GET ELE NOT FOUND");
        confd_data_reply_not_found(tctx);
        return CONFD_OK;
      }

    switch( CONFD_GET_XMLTAG(&kp->v[0][0]))
    {
        case o_ran_fm_fault_id:
            {
           alwaysLog("o_ran_fm_fault_id");
                CONFD_SET_UINT16(&v,100);
            }
            break;
           case o_ran_fm_fault_source:
            {

           alwaysLog("o_ran_fm_fault_source");
                CONFD_SET_STR(&v,"SOURCE");
            }
            break;
        case o_ran_fm_fault_severity:
            {

           alwaysLog("o_ran_fm_fault_severity");
                CONFD_SET_ENUM_VALUE(&v,1);
            }
            break;
        case o_ran_fm_is_cleared:
            {

           alwaysLog("o_ran_fm_is_cleared");
                CONFD_SET_BOOL(&v,1);
            }
            break;
        case o_ran_fm_fault_text:
            {

           alwaysLog("o_ran_fm_fault_text");
                CONFD_SET_STR(&v,"TEXT");
            }
            break;
        case o_ran_fm_event_time:
            {
           alwaysLog("o_ran_fm_event_time");
                    getdatetime(&eventTime);
      CONFD_SET_DATETIME(&v,eventTime);
            }
            break;
        case o_ran_fm_name:
            alwaysLog("o_ran_fm_name");
                CONFD_SET_STR(&v,"NAME");
            break;
      }
    confd_data_reply_value(tctx, &v);
    return CONFD_OK;

    }

     static int exists(struct confd_trans_ctx *tctx,confd_hkeypath_t * keypath)
    {
       alwaysLog("INSIDE EXISTS");
     activeAlarm *ae = find_ae(keypath, tctx->t_opaque);
    if (ae == NULL ) {
        alwaysLog("NOT FOUND");
        confd_data_reply_not_found(tctx);
    } else {
        confd_data_reply_found(tctx);
        alwaysLog(" FOUND");
    }
    return CONFD_OK;

     }

    activeAlarm *find_ae(confd_hkeypath_t *keypath, activeAlarm *dp)
    {
        return (activeAlarm *)(CONFD_GET_INT64(&keypath->v[1][0]));
    }

    static int fm_finish_data(struct confd_trans_ctx *tctx)
    {
    alwaysLog("Inside fm_finish_data");
    activeAlarm *dp = tctx->t_opaque;
    if(dp != NULL)
     free(dp);
    return CONFD_OK;
    }


    #define CALLPOINT_DECL(mib, cp_id, get_elem_cb, 
    get_next_cb,get_object_cb,exists_opt_cb)    \
                {.callpoint = CALLPOINT_ID(mib, cp_id),                        \
                                             .get_elem = get_elem_cb,          \
                                             .get_next = get_next_cb,          \
                                             .get_object = get_object_cb,      \
                                             .exists_optional = exists_opt_cb           }

    void fm_setup(struct confd_daemon_ctx *dctx,    int rsock)
    {
     struct confd_data_cbs dcb[] = {
        CALLPOINT_DECL(o_ran_fm,cb_op_fm,fm_status_get_elem,fm_status_get_next,NULL,exists)
    };

    int i;

    /* Register data callbacks */
    for (i = 0; i < sizeof(dcb) / sizeof(struct confd_data_cbs); i++) {
        if (confd_register_data_cb(dctx, &dcb[i]) != CONFD_OK)
            alwaysLog("Failed to register callpoint \"%s\"", dcb[i].callpoint);
     }

     }
  1. Looks like double free or un-intialized variable problem.
    Make sure that you don’t invoke e.g. finish function explicitly yourself (except for passing it to transaction callback).
    Regular approach -> see ConfD developer log, confd-lib trace logs in app, and try to spot the suspect.
    (e.g. can you see init() invoked before finish() for the transaction? are there any errors preceding the “free()” problem etc.)
    If problem still evades identification, you can try to run data provider app with Valgrind to see some more/less hidden error/risk areas… this is my favorite helper for debugging C apps :slight_smile:

  2. this is most probably just related to/caused by 1.

  3. if you mean after observing 1. / 2., then again most probably yes -> is a consequence -> you can check whether callbacks are still registered at this point due to previous errors e.g. via confd --status

Hi,
I am freeing the memory in finish_data function only which is a callback.I am not explicitly calling the function.
Also i have seen the confd --status and i could see .exists_optional callback is registered(id=cb_op_fm daemonId=18 daemonName=Operational callbacks=get_next,get_elem,exists_optional).

I have tried to print the address holding by dp(pointer to structure) after dynamically allocated memory as below:

 static int fm_init_data(struct confd_trans_ctx *tctx )
{
 activeAlarm *dp ;
if ((dp = malloc(2 *sizeof(activeAlarm))) == NULL)
    return CONFD_ERR;
    alwaysLog("ADDRESS:%p",dp);
memset(dp, 0, 2* sizeof(activeAlarm));
  free(dp);
  return CONFD_ERR;
 }

  tctx->t_opaque =dp;
return CONFD_OK;

}

Similarly, I try to print in get_next and get_elem call back function also. As below:

 activeAlarm *dp=tctx->t_opaque;
  alwaysLog(" GET NEXT ADDRESS:%p",dp);

I found the value collected from init_data is different then other.
ADDRESS:0x4c33d0 —>message from fm_init_data
GET NEXT ADDRESS:0x4836e8
GET ELEM ADDRESS:0x4836e8
FINISH ADDRESS: 0x4836e8

I am not sure if I am missing anything.
devel.log looks OK .
TRACE LOG:

TRACE CALL trans init(thandle=5868,mode=“r”,db=running)2020-08-21 10:50:30 -6- as-confd[11676] 62151.385261 1.461278 oran-fm.c:220 ADDRESS:0x4c33d0
TRACE CALL data get_next(thandle=5868, /active-alarm-list/active-alarms, -1)2020-08-21 10:50:30 -6- as-confd[11676] 62151.732625 0.334174 oran-fm.c:239 GET NEXT ADDRESS:0x4836e8
–> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-id) (100) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-source) (“SOURCE”) --> CONFD_OK
TRACE CALL data get_next(thandle=5868, /active-alarm-list/active-alarms{4732648}/affected-objects, -1)2020-08-21 10:50:30 -6- as-confd[11676] 62151.735993 0.003368 oran-fm.c:239 GET NEXT ADDRESS:0x4836e8
–> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/affected-objects{4732648}/name) (“NAME”) --> CONFD_OK
TRACE CALL data get_next(thandle=5868, /active-alarm-list/active-alarms{4732648}/affected-objects, 1)2020-08-21 10:50:30 -6- as-confd[11676] 62151.736844 0.000853 oran-fm.c:239 GET NEXT ADDRESS:0x4836e8
–> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-severity) (enum<1>) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/is-cleared) (true) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-text) (“TEXT”) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/event-time) (2020-08-21T10:50:30.610692+00:00) --> CONFD_OK
TRACE CALL data get_next(thandle=5868, /active-alarm-list/active-alarms, 1)2020-08-21 10:50:30 -6- as-confd[11676] 62151.738953 0.002108 oran-fm.c:239 GET NEXT ADDRESS:0x4836e8

TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-id) (100) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-source) (“SOURCE”) --> CONFD_OK
TRACE CALL data get_next(thandle=5868, /active-alarm-list/active-alarms{4732648}/affected-objects, -1)2020-08-21 10:50:30 -6- as-confd[11676] 62151.740280 0.001306 oran-fm.c:239 GET NEXT ADDRESS:0x4836e8
–> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/affected-objects{4732648}/name) (“NAME”) --> CONFD_OK
TRACE CALL data get_next(thandle=5868, /active-alarm-list/active-alarms{4732648}/affected-objects, 1)2020-08-21 10:50:30 -6- as-confd[11676] 62151.741128 0.000849 oran-fm.c:239 GET NEXT ADDRESS:0x4836e8
–> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-severity) (enum<1>) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/is-cleared) (true) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/fault-text) (“TEXT”) --> CONFD_OK
TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/event-time) (2020-08-21T10:50:30.615115+00:00) --> CONFD_OK
TRACE CALL data get_next(thandle=5868, /active-alarm-list/active-alarms, 2)2020-08-21 10:50:30 -6- as-confd[11676] 62151.743273 0.002145 oran-fm.c:239 GET NEXT ADDRESS:0x4836e8
–> CONFD_OK
TRACE CALL trans finish(thandle=5868)2020-08-21 10:50:30 -6- as-confd[11676] 62151.747292 0.003992 oran-soft-mgm.c:187 s_finish called
2020-08-21 10:50:30 -6- as-confd[11676] 62151.747434 0.000144 oran-fm.c:122 Inside fm_finish_data
2020-08-21 10:50:30 -6- as-confd[11676] 62151.747455 0.000020 oran-fm.c:124 FINISH ADDRESS: 0x4836e8
free(): invalid pointer

If you can help me in figuring out the root cause it will be really helpful.

Regards,
Biswajit

unfortunately we cannot debug/finetune custom C applications here, rather give hints/tips on confd (confd-lib)…

e.g. not sure if copy&paste error, but the code you pasted in last comment calls free() right after malloc, and thus leads to double free C issue in the end… this line was not there in previous code printout…

i can give pointers no problem for existing examples in confd, or with minor updates/extensions of those, but not for full custom implementations…

TRACE CALL data get_elem(thandle=5868,/active-alarm-list/active-alarms{4732648}/affected-objects{4732648}/name)

There is same key value in both lists on keypath (both active-alams{} and affected-object{}).

This seems strange / possibly wrong, assuming you don’t use same memory to identify oper. data of two different lists (resp. their rows - one parent, one child)… again, this depends on how you want to represent/manage your memory/data in the “backend”… Your pasted code of get_next() does not take input keypath into account, and does not act “differently” depending on which list ConfD is asking about…

Hi Joseph,
Yes correct.I have provided same key for both outer and inner.
Can you please help me in providing different key for both outer list and inner list.

Because in inner list when I am trying to set key in get_next :
CONFD_SET_STR(&v[0],“OBJ”);
Then its not working.

Also I am following the way linuxcfg application is following to subscribe init_data,finish_data.

 static int init_data(struct confd_trans_ctx *tctx)
{
      int i, ret = CONFD_OK;

confd_trans_set_fd(tctx, opworkersock.fd);

for (i = 0; i < num_components && ret == CONFD_OK; i++) {
    if (components[i]->init_data != NULL)
        ret = (*components[i]->init_data)(tctx);
}

if (ret != CONFD_OK) {
    /* Need to call finish for successfully called inits */
    for (i -= 2; i >= 0; i--) {
        if (components[i]->finish_data != NULL)
            (*components[i]->finish_data)(tctx);
    }
}
return ret;
}

static int finish_data(struct confd_trans_ctx *tctx)
{
int i;

for (i = num_components - 1; i >= 0; i--) {
    if (components[i]->finish_data != NULL)
        (*components[i]->finish_data)(tctx);
}
return CONFD_OK;
}

static void register_transpoints()
{

struct confd_trans_cbs tcb;
memset(&tcb, 0, sizeof(tcb));
tcb.init = init_data;
tcb.finish = finish_data;
confd_register_trans_cb(dctx, &tcb);
}

So i hope, the subscription part is not having any issue.Please review this part also.

Regards,
Biswajit

Regards,
Biswajit

 CONFD_SET_INT64(&v[0],(int64_t )dp);
 confd_data_reply_next_key(tctx, &v[0],1, (long)(pos +1));

Is this correct? Do you change dp (which is tctx->t_opaque) in next get_next call?

I suggest you create very simple application (e.g. based on examples.confd/intro/8-c_stats_no_key/arpstat.c) and see how it behaves (perhaps by adding traceh.h - see e.g. examples.confd/intro/11-c_hooks/hooks.c how examples.confd/include/traceh.h is used.