/******************************************************************** * FUNCTION send_discard_changes_pdu_to_server * * Send a <discard-changes> operation to the server * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ status_t send_discard_changes_pdu_to_server (server_cb_t *server_cb) { obj_template_t *rpc; mgr_rpc_req_t *req; val_value_t *reqdata; ses_cb_t *scb; status_t res; xmlns_id_t obj_nsid; req = NULL; reqdata = NULL; res = NO_ERR; if (LOGDEBUG) { log_debug("\nSending <discard-changes> request"); } rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_DISCARD_CHANGES); if (!rpc) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } obj_nsid = obj_get_nsid(rpc); /* construct a method node */ reqdata = xml_val_new_flag(obj_get_name(rpc), obj_nsid); if (!reqdata) { log_error("\nError allocating a new RPC request"); return ERR_INTERNAL_MEM; } scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); } else { req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } } /* if all OK, send the RPC request */ if (res == NO_ERR) { if (LOGDEBUG2) { log_debug2("\nabout to send RPC request with reqdata:"); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); if (res == NO_ERR) { server_cb->command_mode = CMD_MODE_AUTODISCARD; } } /* cleanup and set next state */ if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* send_discard_changes_pdu_to_server */
/******************************************************************** * 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 send_lock_pdu_to_server * * Send a <lock> or <unlock> operation to the server * * INPUTS: * server_cb == server control block to use * lockcb == lock control block to use within server_cb * islock == TRUE for lock; FALSE for unlock * * RETURNS: * status *********************************************************************/ static status_t send_lock_pdu_to_server (server_cb_t *server_cb, lock_cb_t *lockcb, boolean islock) { obj_template_t *rpc, *input; mgr_rpc_req_t *req; val_value_t *reqdata, *targetval, *parmval; ses_cb_t *scb; status_t res; xmlns_id_t obj_nsid; req = NULL; reqdata = NULL; res = NO_ERR; if (LOGDEBUG) { log_debug("\nSending <%s> request", (islock) ? NCX_EL_LOCK : NCX_EL_UNLOCK); } if (islock) { rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_LOCK); } else { rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_UNLOCK); } if (!rpc) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } obj_nsid = obj_get_nsid(rpc); /* get the 'input' section container */ input = obj_find_child(rpc, NULL, YANG_K_INPUT); if (!input) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* construct a method + parameter tree */ reqdata = xml_val_new_struct(obj_get_name(rpc), obj_nsid); if (!reqdata) { log_error("\nError allocating a new RPC request"); return ERR_INTERNAL_MEM; } /* set the [un]lock/input/target node XML namespace */ targetval = xml_val_new_struct(NCX_EL_TARGET, obj_nsid); if (!targetval) { log_error("\nError allocating a new RPC request"); val_free_value(reqdata); return ERR_INTERNAL_MEM; } else { val_add_child(targetval, reqdata); } parmval = xml_val_new_flag(lockcb->config_name, obj_nsid); if (!parmval) { val_free_value(reqdata); return ERR_INTERNAL_MEM; } else { val_add_child(parmval, targetval); } scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); } else { req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } } /* if all OK, send the RPC request */ if (res == NO_ERR) { if (LOGDEBUG2) { log_debug2("\nabout to send RPC request with reqdata:"); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); if (res == NO_ERR) { if (islock) { lockcb->lock_state = LOCK_STATE_REQUEST_SENT; } else { lockcb->lock_state = LOCK_STATE_RELEASE_SENT; } (void)uptime(&lockcb->last_msg_time); server_cb->locks_cur_cfg = lockcb->config_id; } } /* cleanup and set next state */ if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* send_lock_pdu_to_server */
/******************************************************************** * 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 */