/******************************************************************** * FUNCTION rpc_new_msg * * Malloc and initialize a new rpc_msg_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ rpc_msg_t * rpc_new_msg (void) { rpc_msg_t *msg; msg = m__getObj(rpc_msg_t); if (!msg) { return NULL; } memset(msg, 0x0, sizeof(rpc_msg_t)); xml_msg_init_hdr(&msg->mhdr); dlq_createSQue(&msg->rpc_dataQ); msg->rpc_input = val_new_value(); if (!msg->rpc_input) { rpc_free_msg(msg); return NULL; } msg->rpc_top_editop = OP_EDITOP_MERGE; return msg; } /* rpc_new_msg */
/******************************************************************** * FUNCTION new_msg * * Malloc and initialize a new mgr_not_msg_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ static mgr_not_msg_t * new_msg (void) { mgr_not_msg_t *msg; msg = m__getObj(mgr_not_msg_t); if (!msg) { return NULL; } memset(msg, 0x0, sizeof(mgr_not_msg_t)); msg->notification = val_new_value(); if (!msg->notification) { m__free(msg); return NULL; } /* xml_msg_init_hdr(&msg->mhdr); */ return msg; } /* new_msg */
/******************************************************************** * FUNCTION new_reply * * Malloc and initialize a new mgr_rpc_rpy_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ static mgr_rpc_rpy_t * new_reply (void) { mgr_rpc_rpy_t *rpy; rpy = m__getObj(mgr_rpc_rpy_t); if (!rpy) { return NULL; } memset(rpy, 0x0, sizeof(mgr_rpc_rpy_t)); rpy->reply = val_new_value(); if (!rpy->reply) { m__free(rpy); return NULL; } /* xml_msg_init_hdr(&rpy->mhdr); */ return rpy; } /* new_reply */
status_t y_ietf_interfaces_init2(void) { status_t res; cfg_template_t* runningcfg; val_value_t* interfaces_state_val; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } interfaces_state_val = val_find_child(runningcfg->root, "ietf-interfaces", "interfaces-state"); /* Can not coexist with other implementation * of ietf-interfaces. */ if(interfaces_state_val!=NULL) { log_error("\nError: /interfaces-state already present!"); return SET_ERROR(ERR_INTERNAL_VAL); } interfaces_state_val = val_new_value(); if (interfaces_state_val == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } val_init_from_template(interfaces_state_val, interfaces_state_obj); val_init_virtual(interfaces_state_val, get_interfaces_state, interfaces_state_val->obj); val_add_child(interfaces_state_val, runningcfg->root); return res; }
/******************************************************************** * FUNCTION xml_rd_open_file * * Read the value for the specified obj from an open FILE in XML format * * INPUTS: * fp == open FILE control block * obj == object template for the output value * val == address of value for output * * RETURNS: * status *********************************************************************/ status_t xml_rd_open_file (FILE *fp, obj_template_t *obj, val_value_t **val) { xml_node_t top; status_t res; /* get a dummy session control block */ ses_cb_t *scb = ses_new_dummy_scb(); if (!scb) { return ERR_INTERNAL_MEM; } scb->fp = fp; res = xml_get_reader_for_session(my_ses_read_cb, NULL, scb/*context*/, &scb->reader); if(res != NO_ERR) { return res; } /* parse */ *val = val_new_value(); if(*val == NULL) { return ERR_INTERNAL_MEM; } xml_init_node(&top); res = xml_consume_node(scb->reader, &top, TRUE, TRUE); if (res != NO_ERR) { return res; } res = val_parse(scb, obj, &top, *val); scb->fp = NULL; /* skip fclose inside ses_free_scb */ ses_free_scb(scb); xml_clean_node(&top); return res; } /* xml_rd_open_file */
/******************************************************************** * FUNCTION mgr_hello_dispatch * * Handle an incoming <hello> message from the client * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void mgr_hello_dispatch (ses_cb_t *scb, xml_node_t *top) { val_value_t *val; ncx_module_t *mod; obj_template_t *obj; mgr_scb_t *mscb; xml_msg_hdr_t msg; status_t res; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif #ifdef MGR_HELLO_DEBUG if (LOGDEBUG) { log_debug("\nmgr_hello got node"); } if (LOGDEBUG2) { xml_dump_node(top); } #endif mscb = mgr_ses_get_mscb(scb); /* only process this message in hello wait state */ if (scb->state != SES_ST_HELLO_WAIT) { /* TBD: stats update */ if (LOGINFO) { log_info("\nmgr_hello dropped, wrong state for session %d", scb->sid); } return; } /* init local vars */ res = NO_ERR; val = NULL; obj = NULL; xml_msg_init_hdr(&msg); /* get a value struct to hold the server hello msg */ val = val_new_value(); if (!val) { res = ERR_INTERNAL_MEM; } /* get the type definition from the registry */ if (res == NO_ERR) { mod = ncx_find_module(NC_MODULE, NULL); if (mod) { obj = ncx_find_object(mod, MGR_SERVER_HELLO_OBJ); } if (!obj) { /* netconf module should have loaded this definition */ res = SET_ERROR(ERR_INTERNAL_PTR); } } /* parse an server hello message */ if (res == NO_ERR) { res = mgr_val_parse(scb, obj, top, val); } /* examine the server capability list * and it matches the server protocol version */ if (res == NO_ERR) { res = process_server_hello(scb, val); } /* report first error and close session */ if (res != NO_ERR) { if (LOGINFO) { log_info("\nmgr_connect error (%s)\n dropping session %u (a:%u)", get_error_string(res), scb->sid, mscb->agtsid, res); } } else { scb->state = SES_ST_IDLE; if (LOGDEBUG) { log_debug("\nmgr_hello manager hello ok"); } } if (val) { val_free_value(val); } } /* mgr_hello_dispatch */
static status_t add_interfaces_state_entry(char* buf, val_value_t* interfaces_state_val) { /*objs*/ obj_template_t* interface_obj; obj_template_t* name_obj; obj_template_t* statistics_obj; obj_template_t* obj; /*vals*/ val_value_t* interface_val; val_value_t* name_val; val_value_t* statistics_val; val_value_t* val; status_t res=NO_ERR; boolean done; char* name; char* str; char* endptr; unsigned int i; uint64_t counter; int ret; char* counter_names_array[] = { "in-octets", "in-unicast-pkts", "in-errors", "in-discards", NULL/*"in-fifo"*/, NULL/*"in-frames"*/, NULL/*in-compressed*/, "in-multicast-pkts", "out-octets", "out-unicast-pkts", "out-errors", "out-discards", NULL/*"out-fifo"*/, NULL/*out-collisions*/, NULL/*out-carrier*/, NULL/*out-compressed*/ }; /* get the start of the interface name */ str = buf; while (*str && isspace(*str)) { str++; } if (*str == '\0') { /* not expecting a line with just whitespace on it */ return ERR_NCX_SKIPPED; } else { name = str++; } /* get the end of the interface name */ while (*str && *str != ':') { str++; } if (*str != ':') { /* expected e.g. eth0: ...*/ return ERR_NCX_SKIPPED; } else { *str=0; str++; } /* /interfaces-state/interface */ interface_obj = obj_find_child(interfaces_state_val->obj, "ietf-interfaces", "interface"); assert(interface_obj != NULL); interface_val = val_new_value(); if (interface_val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(interface_val, interface_obj); val_add_child(interface_val, interfaces_state_val); /* /interfaces-state/interface/name */ name_obj = obj_find_child(interface_obj, "ietf-interfaces", "name"); assert(name_obj != NULL); name_val = val_new_value(); if (name_val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(name_val, name_obj); res = val_set_simval_obj(name_val, name_obj, name); val_add_child(name_val, interface_val); res = val_gen_index_chain(interface_obj, interface_val); assert(res == NO_ERR); /* /interfaces-state/interface/statistics */ statistics_obj = obj_find_child(interface_obj, "ietf-interfaces", "statistics"); assert(statistics_obj != NULL); statistics_val = val_new_value(); if (statistics_val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(statistics_val, statistics_obj); val_add_child(statistics_val, interface_val); done = FALSE; for(i=0;i<(sizeof(counter_names_array)/sizeof(char*));i++) { endptr = NULL; counter = strtoull((const char *)str, &endptr, 10); if (counter == 0 && str == endptr) { /* number conversion failed */ log_error("Error: /proc/net/dev number conversion failed."); return ERR_NCX_OPERATION_FAILED; } if(counter_names_array[i]!=NULL) { obj = obj_find_child(statistics_obj, "ietf-interfaces", counter_names_array[i]); assert(obj != NULL); val = val_new_value(); if (val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(val, obj); VAL_UINT64(val) = counter; val_add_child(val, statistics_val); } str = (xmlChar *)endptr; if (*str == '\0' || *str == '\n') { break; } } return res; }
/******************************************************************** * FUNCTION parse_parm * * Parse, and fill one val_value_t struct during * processing of a parmset * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == token chain * val == container val to fill in * keepvals == TRUE to save existing parms in 'ps', as needed * FALSE to overwrite old parms in 'ps', as needed * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_parm (tk_chain_t *tkc, val_value_t *val, boolean keepvals) { obj_template_t *obj; const xmlChar *modname; val_value_t *curparm, *newparm; status_t res; ncx_iqual_t iqual; boolean match, usewarning, isdefault; /* get the next token, which must be a TSTRING * representing the parameter name */ if (TK_CUR_TYP(tkc) != TK_TT_TSTRING) { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "parameter name"); return res; } curparm = NULL; usewarning = ncx_warning_enabled(ERR_NCX_CONF_PARM_EXISTS); /* check if this TSTRING is a parameter in this parmset * make sure to always check for prefix:identifier * This is automatically processed in tk.c */ if (TK_CUR_MOD(tkc)) { modname = xmlns_get_module (xmlns_find_ns_by_prefix(TK_CUR_MOD(tkc))); if (modname) { curparm = val_find_child(val, modname, TK_CUR_VAL(tkc)); } } else { curparm = val_find_child(val, val_get_mod_name(val), TK_CUR_VAL(tkc)); } if (curparm) { obj = curparm->obj; } else { obj = obj_find_child(val->obj, TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); } if (!obj) { res = ERR_NCX_UNKNOWN_PARM; if (TK_CUR_MOD(tkc)) { log_error("\nError: parameter '%s:%s' not found", TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); } else { log_error("\nError: parameter '%s' not found", TK_CUR_VAL(tkc)); } ncx_conf_exp_err(tkc, res, "parameter name"); return res; } /* got a valid parameter name, now create a new parm * even if it may not be kept. There are corner-cases * that require the new value be parsed before knowing * if a parm value is a duplicate or not */ newparm = val_new_value(); if (!newparm) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, NULL, res); return res; } val_init_from_template(newparm, obj); /* parse the parameter value */ res = parse_val(tkc, obj, newparm); if (res != NO_ERR) { val_free_value(newparm); return res; } /* check if a potential current value exists, or just * add the newparm to the parmset */ if (curparm) { isdefault = val_set_by_default(curparm); iqual = obj_get_iqualval(obj); if (iqual == NCX_IQUAL_ONE || iqual == NCX_IQUAL_OPT) { /* only one allowed, check really a match */ match = TRUE; if (val_has_index(curparm) && !val_index_match(newparm, curparm)) { match = FALSE; } if (!match) { val_add_child(newparm, val); } else if (isdefault) { dlq_remove(curparm); val_free_value(curparm); val_add_child(newparm, val); } else if (keepvals) { if (usewarning) { /* keep current value and toss new value */ log_warn("\nWarning: Parameter '%s' already exists. " "Not using new value\n", curparm->name); if (LOGDEBUG2) { val_dump_value(newparm, NCX_DEF_INDENT); log_debug2("\n"); } } val_free_value(newparm); } else { if (usewarning) { /* replace current value and warn old value tossed */ log_warn("\nconf: Parameter '%s' already exists. " "Overwriting with new value\n", curparm->name); if (LOGDEBUG2) { val_dump_value(newparm, NCX_DEF_INDENT); log_debug2("\n"); } } dlq_remove(curparm); val_free_value(curparm); val_add_child(newparm, val); } } else { /* mutliple instances allowed */ val_add_child(newparm, val); } } else { val_add_child(newparm, val); } return NO_ERR; } /* parse_parm */
/******************************************************************** * FUNCTION parse_val * * Parse, and fill one val_value_t struct during * processing of a text config file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * The value name is the current token. * Based on the value typdef, the res of the tokens * comprising the value statement will be processed * * INPUTS: * tkc == token chain * obj == the object template struct to use for filling in 'val' * val == initialized value struct, without any value, * which will be filled in by this function * nsid == namespace ID to use for this value * valname == name of the value struct * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_val (tk_chain_t *tkc, obj_template_t *obj, val_value_t *val) { obj_template_t *chobj; val_value_t *chval; const xmlChar *valname, *useval; typ_def_t *typdef; status_t res; ncx_btype_t btyp; boolean done; xmlns_id_t nsid; btyp = obj_get_basetype(obj); nsid = obj_get_nsid(obj); valname = obj_get_name(obj); typdef = obj_get_typdef(obj); /* check if there is an index clause expected */ if (typ_has_index(btyp)) { res = parse_index(tkc, obj, val, nsid); if (res != NO_ERR) { return res; } } /* get next token, NEWLINE is significant at this point */ res = adv_tk(tkc); if (res != NO_ERR) { return res; } /* the current token should be the value for a leaf * or a left brace for the start of a complex type * A NEWLINE is treated as if the user entered a * zero-length string for the value. (Unless the * base type is NCX_BT_EMPTY, in which case the NEWLINE * is the expected token */ if (typ_is_simple(btyp)) { /* form for a leaf is: foo [value] NEWLINE */ if (TK_CUR_TYP(tkc)==TK_TT_NEWLINE) { useval = NULL; } else { useval = TK_CUR_VAL(tkc); } res = val_set_simval(val, typdef, nsid, valname, useval); if (res != NO_ERR) { log_error("\nError: '%s' cannot be set to '%s'", valname, (TK_CUR_VAL(tkc)) ? TK_CUR_VAL(tkc) : EMPTY_STRING); if (btyp == NCX_BT_EMPTY) { ncx_conf_exp_err(tkc, res, "empty"); } else { ncx_conf_exp_err(tkc, res, "simple value string"); } return res; } /* get a NEWLINE unless current token is already a NEWLINE */ if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { res = adv_tk(tkc); if (res != NO_ERR) { return res; } if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "\\n"); } } } else { /* complex type is foo { ... } or * foo index1 index2 { ... } * If there is an index, it was already parsed */ res = consume_tk(tkc, TK_TT_LBRACE); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "left brace"); return res; } /* get all the child nodes specified for this complex type */ res = NO_ERR; done = FALSE; while (!done && res==NO_ERR) { /* start out looking for a child node name or a * right brace to end the sub-section */ if (tk_next_typ(tkc)==TK_TT_NEWLINE) { /* skip the NEWLINE token */ (void)adv_tk(tkc); } else if (tk_next_typ(tkc)==TK_TT_RBRACE) { /* found end of sub-section */ done = TRUE; } else { /* get the next token */ res = adv_tk(tkc); if (res != NO_ERR) { continue; } /* make sure cur token is an identifier string * if so, find the child node and call this function * recursively to fill it in and add it to * the parent 'val' */ if (TK_CUR_ID(tkc)) { /* parent 'typdef' must have a child with a name * that matches the current token vale */ chobj = obj_find_child(obj, TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); if (chobj) { chval = val_new_value(); if (!chval) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, NULL, res); } else { val_init_from_template(chval, chobj); res = parse_val(tkc, chobj, chval); if (res == NO_ERR) { val_add_child(chval, val); } else { val_free_value(chval); } } } else { /* string is not a child name in this typdef */ res = ERR_NCX_DEF_NOT_FOUND; ncx_conf_exp_err(tkc, res, "identifier string"); } } else { /* token is not an identifier string */ res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "identifier string"); } } } /* end loop through all the child nodes */ /* expecting a right brace to finish the complex value */ if (res == NO_ERR) { res = consume_tk(tkc, TK_TT_RBRACE); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "right brace"); return res; } } } return res; } /* parse_val */
/******************************************************************** * FUNCTION agt_hello_dispatch * * Handle an incoming <hello> message from the client * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void agt_hello_dispatch (ses_cb_t *scb, xml_node_t *top) { assert( scb && "scb is NULL!" ); assert( top && "top is NULL!" ); if (LOGDEBUG2) { log_debug2("\nagt_hello: got node"); if (LOGDEBUG3) { xml_dump_node(top); } } /* only process this message in hello wait state */ if (scb->state != SES_ST_HELLO_WAIT) { log_info("\nagt_hello dropped, wrong state " "(%d) for session %d", scb->state, scb->sid); mytotals->inBadHellos++; mytotals->droppedSessions++; agt_ses_request_close(scb, scb->sid, SES_TR_BAD_HELLO); return; } /* init local vars */ status_t res = NO_ERR; obj_template_t *obj = NULL; xml_msg_hdr_t msg; xml_msg_init_hdr(&msg); /* get a value struct to hold the client hello msg */ val_value_t *val = val_new_value(); if (!val) { res = ERR_INTERNAL_MEM; } /* get the type definition from the registry */ if (res == NO_ERR) { ncx_module_t *mod = ncx_find_module(NC_MODULE, NULL); if (mod) { obj = ncx_find_object(mod, CLIENT_HELLO_CON); } if (!obj) { /* netconf module should have loaded this definition */ res = SET_ERROR(ERR_INTERNAL_PTR); } } /* parse a manager hello message */ if (res == NO_ERR) { res = agt_val_parse_nc(scb, &msg, obj, top, NCX_DC_STATE, val); } /* check that the NETCONF base capability is included * and it matches the server protocol version */ if (res == NO_ERR) { res = check_manager_hello(scb, val); } /* report first error and close session */ if (res != NO_ERR) { if (LOGINFO) { log_info("\nagt_connect error (%s), dropping session %d", get_error_string(res), scb->sid); } mytotals->inBadHellos++; mytotals->droppedSessions++; agt_ses_request_close(scb, scb->sid, SES_TR_BAD_HELLO); } else { scb->state = SES_ST_IDLE; scb->active = TRUE; /* start the timer for the first rpc request */ (void)time(&scb->last_rpc_time); if (LOGDEBUG) { log_debug("\nSession %d for %s@%s now active", scb->sid, scb->username, scb->peeraddr); if (ses_get_protocol(scb) == NCX_PROTO_NETCONF11) { log_debug_append(" (base:1.1)"); } else { log_debug_append(" (base:1.0)"); } } } if (val) { val_free_value(val); } } /* agt_hello_dispatch */
/******************************************************************** * FUNCTION parse_node * (config mode input received) * Parse the next word using the comstate and session_cb state * Expecting this word to represent a datastore node, not a key or value * * e.g., * interface eth0 mtu 1500 * ^ ^ ^ ^ * node key node value * * INPUTS: * tkc == token chain in progress * session_cb == session control block to use * done == address of return done flag * gotexit == address of return gotexit flag * gotapply == address of return gotapply flag * gotdo == address of return do command flag * * OUTPUTS: * *done == TRUE if parsing done; no more tokens; * ignore unless return NO_ERR * *gotexit == TRUE if got 'exit' command * ignore unless return NO_ERR and *done == TRUE * *gotapply == TRUE if got 'apply' command * ignore unless return NO_ERR and *done == TRUE * *gotdo == TRUE if got 'do' command * RETURNS: * status *********************************************************************/ static status_t parse_node (tk_chain_t *tkc, session_cb_t *session_cb, boolean *done, boolean *gotexit, boolean *gotapply, boolean *gotdo) { *done = FALSE; *gotexit = FALSE; *gotapply = FALSE; *gotdo = FALSE; /* get the next token to use */ status_t res = TK_ADV(tkc); if (res != NO_ERR) { if (res == ERR_NCX_EOF) { *done = TRUE; return NO_ERR; } else { log_error("\nError: Expecting node identifier\n"); return res; } } boolean first = tk_cur_is_first(tkc); /* check if the 'do' form of command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_DO) && tk_next_typ(tkc) != TK_TT_NONE) { *gotdo = TRUE; *done = TRUE; return NO_ERR; } /* check if the 'no' form of command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_NO) && tk_next_typ(tkc) != TK_TT_NONE) { /* get the next token to use as the first identifier */ res = TK_ADV(tkc); if (res != NO_ERR) { log_error("\nError: Expecting identifier token after " "'no' keyword\n"); return res; } first = FALSE; session_cb->config_no_active = TRUE; } /* check if the current token is an identifier */ if (!TK_CUR_ID(tkc)) { log_error("\nError: expecting a node identifier\n"); res = ERR_NCX_WRONG_TKTYPE; return res; } /* check if the exit command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_EXIT) && tk_next_typ(tkc) == TK_TT_NONE) { *done = TRUE; *gotexit = TRUE; return NO_ERR; } /* check if the apply command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_APPLY) && tk_next_typ(tkc) == TK_TT_NONE) { *done = TRUE; *gotapply = TRUE; return NO_ERR; } obj_template_t *startobj = session_cb->config_curobj; obj_template_t *topobj = NULL; if (startobj == NULL) { /* getting top-level config object */ topobj = find_top_obj(session_cb, TK_CUR_VAL(tkc)); if (topobj && !(obj_is_data_db(topobj) && obj_is_config(topobj))) { topobj = NULL; } } else { /* getting child node config object */ topobj = find_child_obj(session_cb, startobj, TK_CUR_VAL(tkc)); if (topobj && !obj_is_config(topobj)) { topobj = NULL; } } if (topobj == NULL) { log_error("\nError: No config object found matching '%s'\n", TK_CUR_VAL(tkc)); return ERR_NCX_UNKNOWN_OBJECT; } /* got a top object (relative to the current context) */ session_cb->config_curobj = topobj; /* make a template object for the edit operation for non-terminals */ val_value_t *newval = NULL; if (!obj_is_leafy(topobj)) { newval = val_new_value(); if (newval == NULL) { log_error("\nError: malloc failed for new value\n"); return ERR_INTERNAL_MEM; } val_init_from_template(newval, topobj); add_enode(session_cb, newval); } else if (session_cb->config_no_active && obj_is_leaf(topobj)) { /* no ... foo-leaf */ newval = xml_val_new_flag(obj_get_name(topobj), obj_get_nsid(topobj)); if (newval == NULL) { log_error("\nError: malloc failed for new value\n"); return ERR_INTERNAL_MEM; } add_enode(session_cb, newval); } if (obj_is_list(topobj)) { boolean anykeys = FALSE; obj_key_t *newkey = obj_first_key(topobj); while (newkey) { anykeys = TRUE; /* get the next token to use as the key value */ res = TK_ADV(tkc); if (res != NO_ERR) { log_error("\nError: Expecting value for '%s' key leaf\n", obj_get_name(newkey->keyobj)); return res; } /* parse the value as a key value */ /*** do something different if TK_CUR_MOD(tkc) is not NULL ***/ val_value_t *keyval = val_make_simval_obj(newkey->keyobj, TK_CUR_VAL(tkc), &res); if (res != NO_ERR) { log_error("\nError: Invalid value for '%s' key leaf\n", obj_get_name(newkey->keyobj)); val_free_value(keyval); return res; } /* save keyval */ if (newval) { val_add_child(keyval, newval); } else { val_free_value(keyval); } /* set up the next key in the list */ newkey = obj_next_key(newkey); } if (anykeys) { res = val_gen_index_chain(topobj, newval); } } if (res == NO_ERR && obj_is_leafy(topobj)) { /* expecting a value node to follow or done if 'no' command */ *done = TRUE; } return res; } /* parse_node */