예제 #1
0
/********************************************************************
* FUNCTION y_starter_starter_get_load_invoke
* 
* RPC invocation phase
* All constraints have passed at this point.
* Call device instrumentation code in this function.
* 
* INPUTS:
*     see agt/agt_rpc.h for details
* 
* RETURNS:
*     error status
********************************************************************/
static status_t y_starter_starter_get_load_invoke (
    ses_cb_t *scb,
    rpc_msg_t *msg,
    xml_node_t *methnode)
{
    status_t res = NO_ERR;


    /* remove the next line if scb is used */
    (void)scb;

    /* remove the next line if msg is used */
    (void)msg;

    /* remove the next line if methnode is used */
    (void)methnode;

    /* invoke your device instrumentation code here */
    /*********************** THIS PART IS ADDED MANUALLY BY LEVI *************************/ 
	//--------- GET LOAD DATA ----------------
	//it looks like this: "0.01 0.14 0.12 1/309 6531"
	FILE *fp;
	int status;
	char load[30];
	
	//open the command for reading
	fp = popen("cat /proc/loadavg", "r");
	if(fp == NULL)
	{
		log_info("\nFailed to get LOAD\n");
			
	}
	else 
	{
		while(fgets(load, sizeof(load)-1, fp) != NULL)
		{
			log_info("\nRead data from cli: %s\n", load);		
		}	
		
		//closing
		pclose(fp);
	}
	//##########################################
	  
	  int numberOfLoadParams = 5;
	  char *load_params[numberOfLoadParams];
	  int n=0;
	  int nn;
	  
	  char *ds = strdup(load);
	  
	  load_params[n]=strtok(ds, " ");
	  //log_info("load_params[%d]:%s",n,load_params[n]);
	  while(load_params[n] && (n < (numberOfLoadParams-1)))
	  {
			load_params[++n] = strtok(NULL, " ");
//			log_info("[LOG]load_params[%d]:%s",n,load_params[n]);
	  }
	  
	  	
	 	
    
		val_value_t *childval = NULL;
    	childval = agt_make_list(
    				obj_find_child(starter_get_load_obj,y_starter_M_starter,"output"),
    				y_starter_N_load,
    				&res);
		if(childval != NULL)
		{
			log_info("\nList found and initialized");		
		}
		
		val_value_t *a = NULL;
/*		char loadOne[40] = "load_One";*/
		a = agt_make_leaf(
			childval->obj,
			y_starter_N_loadOne,
			(const xmlChar*)load_params[0],
			&res);
		if(a!=NULL)
		{
			log_info("\nloadOne filled with ");
			log_info(load_params[0]);

			val_add_child(a,childval);		
		}
		else 
		{
			return res;		
		}
		
		
/*		char loadFive[40] = "load_Five";*/
		a = agt_make_leaf(
			childval->obj,
			y_starter_N_loadFive,
			(const xmlChar*)load_params[1],
			&res);
		if(a!=NULL)
		{
			log_info("\nloadFive filled with ");
			log_info(load_params[1]);

			val_add_child(a,childval);		
		}
		else 
		{
			return res;		
		}
		
		
/*		char loadFifteen[40] = "load_Fifteen";*/
		a = agt_make_leaf(
			childval->obj,
			y_starter_N_loadFifteen,
			(const xmlChar*)load_params[2],
			&res);
		if(a!=NULL)
		{
			log_info("\nloadFifteen filled with ");
			log_info(load_params[2]);

			val_add_child(a,childval);		
		}
		else 
		{
			return res;		
		}
		
/*		char processesCE[40] = "processesCurrentlyExists";*/
		a = agt_make_leaf(
			childval->obj,
			y_starter_N_processesCurrentlyExists,
			(const xmlChar*)load_params[3],
			&res);
		if(a!=NULL)
		{
			log_info("\nprocessesCE filled with ");
			log_info(load_params[3]);

			val_add_child(a,childval);		
		}
		else 
		{
			return res;		
		}
		
		/*log_info("PID: %s", load_params[4]);*/
		//we need to remove the trailing \n from the end
		char* pid = strtok(load_params[4],"\n");
		a = agt_make_leaf(
			childval->obj,
			y_starter_N_pid,
			(const xmlChar*)pid,
			&res);
		if(a!=NULL)
		{
			log_info("\npid filled with ");
			log_info(pid);

			val_add_child(a,childval);		
		}
		else 
		{
			return res;		
		}

		free(ds);

		dlq_enque((void*)childval,&(msg->rpc_dataQ));

/*	 // ------------------------ CREATING A CUSTOM RPC REPLY -------------------------------
	
	 //declare a val_value_t * paramter   
    val_value_t *rpc_output = NULL;
    //making a leaf
    //	1.param -> we need to find the corresponding output element defined in the yang
    //		starter_get_load_obj is created automatically, since we have a get_load rpc in our module called starter -> starter_get_load_obj
    //		y_starter_M_starter corresponds to the module (you need to modify both strings 'starter' to your module name)
    //		"output" -> is the output part of the rpc (leave unmodified)
    //	2.param -> y_yourModuleName_N_leafNodeInYourRPCOUTPUT
    //	3.param -> the message you want to send (should be xmlChar*, char, char*)
    //	4. param -> the res variable which is returned by default 
	 rpc_output = agt_make_leaf(
	 		obj_find_child(starter_get_load_obj,y_starter_M_starter,"output"),
	 		y_starter_N_load,	
	 		load,
	 		&res);
	
	 //check that we could create correctly our RPC reply message
	 if (rpc_output!=NULL)
    {
    	//if yes -> put that to the rpc-reply (only rpc_output variable should be modified)
		dlq_enque((void*)rpc_output,&(msg->rpc_dataQ));
    }
    //if, however, something went wrong during creating rpc-reply, we print it into the log
    else 
    {
        log_error(
            "\nError: make leaf failed (%s), cannot send "
            "<load> rpc_reply",
            get_error_string(res));
    } 
    //======================================================================================   */
   
    /********************************** END OF LEVI **************************************/
    return res;

} /* y_starter_starter_get_load_invoke */
예제 #2
0
/********************************************************************
* 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 */
예제 #3
0
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;
}
예제 #4
0
파일: conf.c 프로젝트: 0xDEC0DE8/OpenYuma
/********************************************************************
* 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 */
예제 #5
0
파일: conf.c 프로젝트: 0xDEC0DE8/OpenYuma
/********************************************************************
* 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 */
예제 #6
0
/********************************************************************
* FUNCTION obj_dump_template
*
* Dump the contents of an obj_template_t struct for help text
*
* INPUTS:
*   obj == obj_template to dump help for
*   mode == requested help mode
*   nestlevel == number of levels from the top-level
*                that should be printed; 0 == all levels
*   indent == start indent count
*********************************************************************/
void
    obj_dump_template (obj_template_t *obj,
                       help_mode_t mode,
                       uint32 nestlevel,
                       uint32 indent)
{
    const xmlChar        *val, *keystr;
    obj_template_t       *testobj;
    uint32                count, objnestlevel;
    char                  numbuff[NCX_MAX_NUMLEN];
    boolean               normalpass, neednewline;

#ifdef DEBUG
    if (mode > HELP_MODE_FULL) {
        SET_ERROR(ERR_INTERNAL_VAL);
        return;
    }
#endif

    if (obj == NULL) {
        /* could be called because disabled first child found */
        return;
    }

    if (!obj_has_name(obj) || !obj_is_enabled(obj)) {
        return;
    }

    if (mode == HELP_MODE_NONE) {
        return;
    }

    objnestlevel = obj_get_level(obj);

    if (mode == HELP_MODE_BRIEF && 
        nestlevel > 0 &&
        objnestlevel > nestlevel) {
        return;
    }

    normalpass = (nestlevel && (objnestlevel > nestlevel))
        ? FALSE : TRUE;

    if (obj->objtype == OBJ_TYP_RPCIO || 
        obj->objtype == OBJ_TYP_RPC) {
        help_write_lines(obj_get_name(obj), indent, TRUE);
        count = 0;
    } else if (obj->objtype == OBJ_TYP_CASE) {
        count = obj_enabled_child_count(obj);
    } else {
        count = 2;
    }
    if (count > 1) {
        help_write_lines(obj_get_typestr(obj), indent, TRUE);
        help_write_lines((const xmlChar *)" ", 0, FALSE);
        help_write_lines(obj_get_name(obj), 0, FALSE);
    }

    neednewline = FALSE;

    switch (obj->objtype) {
    case OBJ_TYP_CONTAINER:
    case OBJ_TYP_LIST:
    case OBJ_TYP_CASE:
    case OBJ_TYP_RPC:
    case OBJ_TYP_RPCIO:
    case OBJ_TYP_NOTIF:
        break;
    default:
        /* print type and default for leafy and choice objects */
        if (obj->objtype != OBJ_TYP_CHOICE) {
            help_write_lines((const xmlChar *)" [", 0, FALSE); 
            help_write_lines((const xmlChar *)
                             obj_get_type_name(obj), 0, FALSE);
            help_write_lines((const xmlChar *)"]", 0, FALSE); 
        }

        if (mode != HELP_MODE_BRIEF) {
            val = obj_get_default(obj);
            if (val != NULL) {
                help_write_lines((const xmlChar *)" [d:", 0, FALSE); 
                help_write_lines(val, 0, FALSE);
                help_write_lines((const xmlChar *)"]", 0, FALSE); 
            }
            if (obj_is_key(obj)) {
                help_write_lines((const xmlChar *)" <Key>", 0, FALSE);
            } else if (obj_is_mandatory(obj)) {
                help_write_lines((const xmlChar *)" <Mandatory>", 0, FALSE);
            }
        }
    }

    if (normalpass) {
        val = obj_get_description(obj);
        if (val == NULL) {
            val = obj_get_alt_description(obj);
        }
        if (val != NULL) {
            switch (mode) {
            case HELP_MODE_BRIEF:
                if (obj->objtype == OBJ_TYP_RPC || 
                    obj->objtype == OBJ_TYP_NOTIF) {
                    help_write_lines_max(val, indent+NCX_DEF_INDENT,
                                         TRUE, HELP_MODE_BRIEF_MAX); 
                }
                break;
            case HELP_MODE_NORMAL:
                help_write_lines_max(val, indent+NCX_DEF_INDENT,
                                     TRUE, HELP_MODE_NORMAL_MAX); 
                break;
            case HELP_MODE_FULL:
                help_write_lines(val, indent+NCX_DEF_INDENT, TRUE); 
                break;
            default:
                SET_ERROR(ERR_INTERNAL_VAL);
                return;
            }
        }
    }

    switch (obj->objtype) {
    case OBJ_TYP_CONTAINER:
        switch (mode) {
        case HELP_MODE_BRIEF:
            neednewline = TRUE;
            break;
        case HELP_MODE_NORMAL:
            if (obj->def.container->presence) {
                help_write_lines((const xmlChar *)" (P)", 0, FALSE); 
            } else {
                help_write_lines((const xmlChar *)" (NP)", 0, FALSE); 
            }
            testobj = obj_get_default_parm(obj);
            if (testobj) {
                help_write_lines((const xmlChar *)"default parameter: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                help_write_lines(obj_get_name(testobj), 0, FALSE);
            }
            break;
        case HELP_MODE_FULL:
            if (obj->def.container->presence) {
                help_write_lines((const xmlChar *)"presence: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                help_write_lines(obj->def.container->presence, 0, FALSE);
            }
            testobj = obj_get_default_parm(obj);
            if (testobj) {
                help_write_lines((const xmlChar *)"default parameter: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                help_write_lines(obj_get_name(testobj), 0, FALSE);
            }
            if (mode == HELP_MODE_FULL) {
                /*** add mustQ ***/;
            }
            break;
        default:
            SET_ERROR(ERR_INTERNAL_VAL);
            return;
        }
        obj_dump_datadefQ(obj->def.container->datadefQ, 
                          mode, nestlevel, indent+NCX_DEF_INDENT);
        break;
    case OBJ_TYP_ANYXML:
        /* nothing interesting in the typdef to report */
        neednewline = TRUE;
        break;
    case OBJ_TYP_LEAF:
        switch (mode) {
        case HELP_MODE_BRIEF:
            break;
        case HELP_MODE_NORMAL:
            if (normalpass) {
                dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT);
            }
            break;
        case HELP_MODE_FULL:
            dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT);
            val = obj_get_units(obj);
            if (val) {
                help_write_lines((const xmlChar *)"units: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                help_write_lines(val, 0, FALSE);
            }
            break;
        default:
            ;
        }
        break;
    case OBJ_TYP_LEAF_LIST:
        switch (mode) {
        case HELP_MODE_NORMAL:
            if (normalpass) {
                dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT);
            }
            break;
        case HELP_MODE_FULL:
            dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT);
            val = obj_get_units(obj);
            if (val) {
                help_write_lines((const xmlChar *)"units: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                help_write_lines(val, 0, FALSE);
            }
            if (!obj->def.leaflist->ordersys) {
                help_write_lines((const xmlChar *)"ordered-by: user", 
                                 indent+NCX_DEF_INDENT, TRUE); 
            } else {
                help_write_lines((const xmlChar *)"ordered-by: system", 
                                 indent+NCX_DEF_INDENT, TRUE); 
            }
            if (obj->def.leaflist->minset) {
                help_write_lines((const xmlChar *)"min-elements: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                snprintf(numbuff, sizeof(numbuff), "%u",
                         obj->def.leaflist->minelems);
                help_write_lines((const xmlChar *)numbuff, 0, FALSE);
            }
            if (obj->def.leaflist->maxset) {
                help_write_lines((const xmlChar *)"max-elements: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                snprintf(numbuff, sizeof(numbuff), "%u",
                         obj->def.leaflist->maxelems);
                help_write_lines((const xmlChar *)numbuff, 0, FALSE);
            }
            break;
        default:
            ;
        }
        break;
    case OBJ_TYP_CHOICE:
        if (mode == HELP_MODE_BRIEF) {
            neednewline = TRUE;
            break;
        }
        count = obj_enabled_child_count(obj);
        if (count) {
            obj_dump_datadefQ(obj_get_datadefQ(obj),
                              mode, nestlevel, indent+NCX_DEF_INDENT);
        }
        break;
    case OBJ_TYP_CASE:
        if (mode == HELP_MODE_BRIEF) {
            neednewline = TRUE;
            break;
        }
        count = obj_enabled_child_count(obj);
        if (count > 1) {
            obj_dump_datadefQ(obj_get_datadefQ(obj), 
                              mode, nestlevel, indent+NCX_DEF_INDENT);
        } else if (count == 1) {
            testobj = obj_first_child(obj);
            if (testobj) {
                obj_dump_template(testobj, mode, nestlevel, indent);
            }
        } /* else skip this case */
        break;
    case OBJ_TYP_LIST:
        switch (mode) {
        case HELP_MODE_BRIEF:
            break;
        case HELP_MODE_NORMAL:
        case HELP_MODE_FULL:
            keystr = obj_get_keystr(obj);
            if (keystr != NULL) {
                help_write_lines((const xmlChar *)"key: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                help_write_lines(keystr, 0, FALSE);
            }
            if (!obj->def.list->ordersys) {
                help_write_lines((const xmlChar *)"ordered-by: user", 
                                 indent+NCX_DEF_INDENT, TRUE); 
            }

            if (mode == HELP_MODE_NORMAL) {
                break;
            }

            if (obj->def.list->minset) {
                help_write_lines((const xmlChar *)"min-elements: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                snprintf(numbuff, sizeof(numbuff), "%u",
                         obj->def.list->minelems);
                help_write_lines((const xmlChar *)numbuff, 0, FALSE);
            }
            if (obj->def.list->maxset) {
                help_write_lines((const xmlChar *)"max-elements: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                snprintf(numbuff, sizeof(numbuff), "%u",
                         obj->def.list->maxelems);
                help_write_lines((const xmlChar *)numbuff, 0, FALSE);
            }
            break;
        default:
            ;
        }
        if (mode != HELP_MODE_BRIEF) {
            obj_dump_datadefQ(obj_get_datadefQ(obj), 
                              mode, nestlevel, indent+NCX_DEF_INDENT);
        }
        break;
    case OBJ_TYP_RPC:
        testobj = obj_find_child(obj, NULL, YANG_K_INPUT);
        if (testobj && obj_enabled_child_count(testobj)) {
            obj_dump_template(testobj, mode, nestlevel,
                              indent+NCX_DEF_INDENT);
        }

        testobj = obj_find_child(obj, NULL, YANG_K_OUTPUT);
        if (testobj && obj_enabled_child_count(testobj)) {
            obj_dump_template(testobj, mode, nestlevel,
                              indent+NCX_DEF_INDENT);
        }
        help_write_lines((const xmlChar *)"\n", 0, FALSE);
        break;
    case OBJ_TYP_RPCIO:
        if (mode != HELP_MODE_BRIEF) {
            testobj = obj_get_default_parm(obj);
            if (testobj && obj_is_enabled(testobj)) {
                help_write_lines((const xmlChar *)"default parameter: ", 
                                 indent+NCX_DEF_INDENT, TRUE); 
                help_write_lines(obj_get_name(testobj), 0, FALSE);
            } else {
                neednewline = FALSE;
            }
        }
        obj_dump_datadefQ(obj_get_datadefQ(obj), 
                          mode, nestlevel, indent+NCX_DEF_INDENT);
        break;
    case OBJ_TYP_NOTIF:
        obj_dump_datadefQ(obj_get_datadefQ(obj),
                          mode, nestlevel, indent+NCX_DEF_INDENT);
        break;
    case OBJ_TYP_AUGMENT:
    case OBJ_TYP_USES:
    case OBJ_TYP_REFINE:
    default:
        SET_ERROR(ERR_INTERNAL_VAL);
    }

    if (neednewline) {
        help_write_lines(NULL, 0, TRUE);
    }

}   /* obj_dump_template */