/******************************************************************** * FUNCTION do_unset (local RPC) * * unset def * * Handle the unset command; remove the specified alias * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the unset command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_unset (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; status_t res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR && valset) { parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_NAME); if (parm) { const xmlChar *varstr = VAL_STR(parm); alias_cb_t *alias = find_alias(varstr, xml_strlen(varstr)); if (alias) { dlq_remove(alias); free_alias(alias); log_info("\nDeleted alias '%s'\n", varstr); } else { res = ERR_NCX_INVALID_VALUE; log_error("\nError: unknown alias '%s'\n", varstr); } } /* else missing parameter already reported */ } /* else no valset already reported */ if (valset) { val_free_value(valset); } return res; } /* do_unset */
void agt_not_queue_notification_cb_unregister( const xmlChar *modname ) { assert( modname ); agt_cb_queue_notification_set_t* cbSet = find_callback_set( modname ); if ( cbSet ) { dlq_remove( cbSet ); free_callback_set( cbSet ); } }
void agt_commit_complete_unregister( const xmlChar *modname ) { assert( modname ); agt_cb_commit_complete_set_t* cbSet = find_callback_set( modname ); if ( cbSet ) { dlq_remove( cbSet ); free_callback_set( cbSet ); } }
/******************************************************************** * FUNCTION unregister_notif_event_handler * * Unregister an event callback function * This is optional -- data structures will get cleaned up * when the program terminates * Assumes function is registered once!!! * Caller must make sure to register each callback only once * or call this function multiple times * * INPUTS: * modname == module defining the notification * event == notification event name * cbfn == function to unregister *********************************************************************/ void unregister_notif_event_handler (const xmlChar *modname, const xmlChar *event, yangcli_notif_cbfn_t cbfn) { event_cb_t *cb = find_event_cb(modname, event, NULL); while (cb) { if (cb->cbfn == cbfn) { dlq_remove(cb); free_event_cb(cb); return; } cb = find_event_cb(modname, event, cb); } } /* unregister_notif_event_handler */
/******************************************************************** * FUNCTION mgr_rpc_timeout_requestQ * * Clean the request Q of mgr_rpc_req_t entries * Only remove the entries that have timed out * * returning number of msgs timed out * need a callback-based cleanup later on * to support N concurrent requests per agent * * INPUTS: * reqQ == Q of entries to check * * RETURNS: * number of request timed out *********************************************************************/ uint32 mgr_rpc_timeout_requestQ (dlq_hdr_t *reqQ) { mgr_rpc_req_t *req, *nextreq; time_t timenow; double timediff; uint32 deletecount; #ifdef DEBUG if (!reqQ) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif deletecount = 0; (void)uptime(&timenow); for (req = (mgr_rpc_req_t *)dlq_firstEntry(reqQ); req != NULL; req = nextreq) { nextreq = (mgr_rpc_req_t *)dlq_nextEntry(req); if (!req->timeout) { continue; } timediff = difftime(timenow, req->starttime); if (timediff >= (double)req->timeout) { log_info("\nmgr_rpc: deleting timed out request '%s'", req->msg_id); deletecount++; dlq_remove(req); mgr_rpc_free_request(req); } } return deletecount; } /* mgr_rpc_timeout_requestQ */
/******************************************************************** * FUNCTION xml_msg_clean_defns_attr * * Get rid of an xmlns=foo default attribute * * INPUTS: * attrs == xmlns_attrs_t Q to process * * OUTPUTS: * *attrs will be cleaned as needed, * * RETURNS: * status *********************************************************************/ status_t xml_msg_clean_defns_attr (xml_attrs_t *attrs) { xml_attr_t *attr, *nextattr; uint32 len, len2; #ifdef DEBUG if (!attrs) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif len = xml_strlen(XMLNS); for (attr = (xml_attr_t *)xml_first_attr(attrs); attr != NULL; attr = nextattr) { nextattr = (xml_attr_t *)xml_next_attr(attr); len2 = xml_strlen(attr->attr_qname); if (len2 >= len) { if (!xml_strncmp(attr->attr_qname, XMLNS, len)) { if (len == len2) { /* this xmlns=foo is getting toosed so * the netconf NSID can be the default */ dlq_remove(attr); xml_free_attr(attr); return NO_ERR; } /* else xmlns:foo=bar found */ } /* else not xmlns */ } /* else not 'xmlns' */ } return NO_ERR; } /* xml_msg_clean_defns_attr */
/******************************************************************** * 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 agt_ses_process_first_ready * * Check the readyQ and process the first message, if any * * RETURNS: * TRUE if a message was processed * FALSE if the readyQ was empty *********************************************************************/ boolean agt_ses_process_first_ready (void) { ses_cb_t *scb; ses_ready_t *rdy; ses_msg_t *msg; status_t res; uint32 cnt; xmlChar buff[32]; rdy = ses_msg_get_first_inready(); if (!rdy) { return FALSE; } /* get the session control block that rdy is embedded into */ scb = agtses[rdy->sid]; if (scb == NULL) { log_debug("\nagt_ses: session %d gone", rdy->sid); return FALSE; } log_debug2("\nagt_ses msg ready for session %d", scb->sid); /* check the session control block state */ if (scb->state >= SES_ST_SHUTDOWN_REQ) { /* don't process the message or even it mark it * It will be cleaned up when the session is freed */ log_debug("\nagt_ses drop input, session %d shutting down", scb->sid); return TRUE; } /* make sure a message is really there */ msg = (ses_msg_t *)dlq_firstEntry(&scb->msgQ); if (!msg || !msg->ready) { SET_ERROR(ERR_INTERNAL_PTR); log_error("\nagt_ses ready Q message not correct"); if (msg && scb->state != SES_ST_INIT) { /* do not echo the ncx-connect message */ cnt = xml_strcpy(buff, (const xmlChar *)"Incoming msg for session "); snprintf((char *)(&buff[cnt]), sizeof(buff) - cnt, "%u", scb->sid); ses_msg_dump(msg, buff); } return FALSE; } else if (LOGDEBUG2 && scb->state != SES_ST_INIT) { cnt = xml_strcpy(buff, (const xmlChar *)"Incoming msg for session "); snprintf((char *)(&buff[cnt]), sizeof(buff) - cnt, "%u", scb->sid); ses_msg_dump(msg, buff); } /* setup the XML parser */ if (scb->reader) { /* reset the xmlreader */ res = xml_reset_reader_for_session(ses_read_cb, NULL, scb, scb->reader); } else { res = xml_get_reader_for_session(ses_read_cb, NULL, scb, &scb->reader); } /* process the message */ if (res == NO_ERR) { /* process the message * the scb pointer may get deleted !!! */ agt_top_dispatch_msg(&scb); } else { if (LOGINFO) { log_info("\nReset xmlreader failed for session %d (%s)", scb->sid, get_error_string(res)); } agt_ses_kill_session(scb, 0, SES_TR_OTHER); scb = NULL; } if (scb) { /* free the message that was just processed */ dlq_remove(msg); ses_msg_free_msg(scb, msg); /* check if any messages left for this session */ msg = (ses_msg_t *)dlq_firstEntry(&scb->msgQ); if (msg && msg->ready) { ses_msg_make_inready(scb); } } return TRUE; } /* agt_ses_process_first_ready */
/******************************************************************** * FUNCTION mgr_rpc_dispatch * * Dispatch an incoming <rpc-reply> response * handle the <rpc-reply> element * called by mgr_top.c: * This function is registered with top_register_node * for the module 'netconf', top-node 'rpc-reply' * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void mgr_rpc_dispatch (ses_cb_t *scb, xml_node_t *top) { obj_template_t *rpyobj; mgr_rpc_rpy_t *rpy; mgr_rpc_req_t *req; xml_attr_t *attr; xmlChar *msg_id; ncx_module_t *mod; mgr_rpc_cbfn_t handler; ncx_num_t num; status_t res; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* init local vars */ res = NO_ERR; msg_id = NULL; req = NULL; /* make sure any real session has been properly established */ if (scb->type != SES_TYP_DUMMY && scb->state != SES_ST_IDLE) { log_error("\nError: mgr_rpc: skipping incoming message '%s'", top->qname); mgr_xml_skip_subtree(scb->reader, top); return; } /* check if the reply template is already cached */ rpyobj = NULL; mod = ncx_find_module(NC_MODULE, NULL); if (mod != NULL) { rpyobj = ncx_find_object(mod, NC_RPC_REPLY_TYPE); } if (rpyobj == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); mgr_xml_skip_subtree(scb->reader, top); return; } /* get the NC RPC message-id attribute; should be present * because the send-rpc function put a message-id in <rpc> */ attr = xml_find_attr(top, 0, NCX_EL_MESSAGE_ID); if (attr && attr->attr_val) { msg_id = xml_strdup(attr->attr_val); } if (msg_id == NULL) { mgr_xml_skip_subtree(scb->reader, top); log_info("\nmgr_rpc: incoming message with no message-id"); return; } /* the current node is 'rpc-reply' in the netconf namespace * First get a new RPC reply struct */ rpy = new_reply(); if (rpy == NULL) { m__free(msg_id); log_error("\nError: mgr_rpc: skipping incoming message"); mgr_xml_skip_subtree(scb->reader, top); return; } else { rpy->msg_id = msg_id; } /* get the NCX RPC group-id attribute if present */ attr = xml_find_attr(top, xmlns_ncx_id(), NCX_EL_GROUP_ID); if (attr && attr->attr_val) { res = ncx_decode_num(attr->attr_val, NCX_BT_UINT32, &num); if (res == NO_ERR) { rpy->group_id = num.u; } } /* find the request that goes with this reply */ if (rpy->msg_id != NULL) { req = find_request(scb, rpy->msg_id); if (req == NULL) { #ifdef MGR_RPC_DEBUG log_debug("\nmgr_rpc: got request found for msg (%s) " "on session %d", rpy->msg_id, scb->sid); #endif mgr_xml_skip_subtree(scb->reader, top); mgr_rpc_free_reply(rpy); return; } else { dlq_remove(req); } } /* have a request/reply pair, so parse the reply * as a val_value_t tree, stored in rpy->reply */ rpy->res = mgr_val_parse_reply(scb, rpyobj, (req != NULL) ? req->rpc : ncx_get_gen_anyxml(), top, rpy->reply); if (rpy->res != NO_ERR && LOGINFO) { log_info("\nmgr_rpc: got invalid reply on session %d (%s)", scb->sid, get_error_string(rpy->res)); } /* check that there is nothing after the <rpc-reply> element */ if (rpy->res==NO_ERR && !xml_docdone(scb->reader) && LOGINFO) { log_info("\nmgr_rpc: got extra nodes in reply on session %d", scb->sid); } /* invoke the reply handler */ if (req != NULL) { handler = (mgr_rpc_cbfn_t)req->replycb; (*handler)(scb, req, rpy); } /* only reset the session state to idle if was not changed * to SES_ST_SHUTDOWN_REQ during this RPC call */ if (scb->state == SES_ST_IN_MSG) { scb->state = SES_ST_IDLE; } #ifdef MGR_RPC_DEBUG print_errors(); clear_errors(); #endif } /* mgr_rpc_dispatch */
/******************************************************************** * FUNCTION mgr_rpc_send_request * * Send an <rpc> request to the agent on the specified session * non-blocking send, reply function will be called when * one is received or a timeout occurs * * INPUTS: * scb == session control block * req == request to send * rpyfn == reply callback function * * RETURNS: * status *********************************************************************/ status_t mgr_rpc_send_request (ses_cb_t *scb, mgr_rpc_req_t *req, mgr_rpc_cbfn_t rpyfn) { xml_msg_hdr_t msg; xml_attr_t *attr; status_t res; boolean anyout; xmlns_id_t nc_id; #ifdef DEBUG if (!scb || !req || !rpyfn) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif #ifdef MGR_HELLO_DEBUG log_debug2("\nmgr sending RPC request %s on session %d", req->msg_id, scb->sid); #endif anyout = FALSE; xml_msg_init_hdr(&msg); nc_id = xmlns_nc_id(); /* make sure the message-id attribute is not already present */ attr = xml_find_attr_q(&req->attrs, 0, NCX_EL_MESSAGE_ID); if (attr) { dlq_remove(attr); xml_free_attr(attr); } /* setup the prefix map with the NETCONF (and maybe NCX) namespace */ res = xml_msg_build_prefix_map(&msg, &req->attrs, FALSE, (req->data->nsid == xmlns_ncx_id())); /* add the message-id attribute */ if (res == NO_ERR) { res = xml_add_attr(&req->attrs, 0, NCX_EL_MESSAGE_ID, req->msg_id); } /* set perf timestamp in case response timing active */ gettimeofday(&req->perfstarttime, NULL); /* send the <?xml?> directive */ if (res == NO_ERR) { res = ses_start_msg(scb); } /* start the <rpc> element */ if (res == NO_ERR) { anyout = TRUE; xml_wr_begin_elem_ex(scb, &msg, 0, nc_id, NCX_EL_RPC, &req->attrs, ATTRQ, 0, START); } /* send the method and parameters */ if (res == NO_ERR) { xml_wr_full_val(scb, &msg, req->data, NCX_DEF_INDENT); } /* finish the <rpc> element */ if (res == NO_ERR) { xml_wr_end_elem(scb, &msg, nc_id, NCX_EL_RPC, 0); } /* finish the message */ if (anyout) { ses_finish_msg(scb); } if (res == NO_ERR) { req->replycb = rpyfn; add_request(scb, req); } xml_msg_clean_hdr(&msg); return res; } /* mgr_rpc_send_request */