Esempio n. 1
0
/*! Make a notify subscription to backend and un/register callback for return messages.
 * 
 * @param[in] h      Clicon handle
 * @param[in] cvv    Not used
 * @param[in] arg    A string with <log stream name> <stream status> [<format>]
 * where <status> is "0" or "1"
 * and   <format> is XXX
 * Example code: Start logging of mystream and show logs as xml
 * @code
 * cmd("comment"), cli_notify("mystream","1","xml"); 
 * @endcode
 * XXX: format is a memory leak
 */
int
cli_notify(clicon_handle h, 
	   cvec         *cvv, 
	   cvec         *argv)
{
    char            *stream = NULL;
    int              retval = -1;
    int              status;
    char            *formatstr = NULL;
    enum format_enum format = FORMAT_TEXT;

    if (cvec_len(argv) != 2 && cvec_len(argv) != 3){
	clicon_err(OE_PLUGIN, 0, "Requires arguments: <logstream> <status> [<format>]");
	goto done;
    }
    stream = cv_string_get(cvec_i(argv, 0));
    status  = atoi(cv_string_get(cvec_i(argv, 1)));
    if (cvec_len(argv) > 2){
	formatstr = cv_string_get(cvec_i(argv, 2));
	format = format_str2int(formatstr);
    }
    if (cli_notification_register(h, 
				  stream, 
				  format,
				  "", 
				  status, 
				  cli_notification_cb, 
				  (void*)format) < 0)
	goto done;

    retval = 0;
  done:
    return retval;
}
Esempio n. 2
0
/*! Copy from one database to another, eg running->startup
 * @param[in] argv  a string: "<db1> <db2>" Copy from db1 to db2
 */
int
db_copy(clicon_handle h, 
	cvec         *cvv, 
	cvec         *argv)
{
    char *db1;
    char *db2;

    db1 = cv_string_get(cvec_i(argv, 0));
    db2 = cv_string_get(cvec_i(argv, 1));
    return clicon_rpc_copy_config(h, db1, db2);
}
Esempio n. 3
0
/*! Delete all elements in a database 
 * Utility function used by cligen spec file
 */
int
delete_all(clicon_handle h, 
	   cvec         *cvv, 
	   cvec         *argv)
{
    char            *dbstr;
    int              retval = -1;

    if (cvec_len(argv) != 1){
	clicon_err(OE_PLUGIN, 0, "Requires one element: dbname");
	goto done;
    }
    dbstr = cv_string_get(cvec_i(argv, 0));
    if (strcmp(dbstr, "running") != 0 && 
	strcmp(dbstr, "candidate") != 0 &&
	strcmp(dbstr, "startup") != 0){
	clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);	
	goto done;
    }
    if (clicon_rpc_delete_config(h, dbstr) < 0)
	goto done;
    retval = 0;
  done:
    return retval;
}
Esempio n. 4
0
/*! General callback for executing shells. 
 * The argument is a command followed by arguments as defined in the input syntax.
 * Simple example:
 *   CLIgen input syntax:     <a type:int32>, cligen_exec_cb("ls ${a}");
 *   CLI input:               > 42
 *   Shell command:           ls 42
 * More advanced example:
 *   CLIgen input syntax:     [<a type:int> | <b type:ipv4addr>], 
 *                                cligen_exec_cb("foo.sh ${a:-99} ${b:-1.2.3.4}");
 *   CLI input:               > 22
 *   Shell command:           foo.sh 22 1.2.3.4
 *   CLI input:               > 2.3.4.5
 *   Shell command:           foo.sh 99 1.2.3.4.
 */
int
cligen_exec_cb(cligen_handle handle, cvec *cvv, cvec *argv)
{
    cg_var *cv = NULL;
    char    buf[64];
    int     pid;
    int     ret;
    int     status;

    if (argv == NULL)
	return 0;
    if ((pid = fork()) == 0){ /* child */
	while ((cv = cvec_each1(cvv, cv)) != NULL) {
	    if (cv_const_get(cv))
		continue;
	    cv2str(cv, buf, sizeof(buf)-1);
	    setenv(cv_name_get(cv), buf, 1 );
	}
	cv2str(cvec_i(argv, 0), buf, sizeof(buf)-1);
	ret = system(buf);
	exit(0);
    }
    /* Wait for child to finish */
    if(waitpid (pid, &status, 0) == pid)
	ret = WEXITSTATUS(status);
    else
	ret = -1;
    return ret;
}
Esempio n. 5
0
/*
 * Command without assigned callback
 */
int
unknown(cligen_handle h, cvec *vars, cg_var *arg)
{
    cg_var *cv = cvec_i(vars, 0);

    printf("The command has no assigned callback: %s\n", cv_string_get(cv));
    return 0;
}
Esempio n. 6
0
/*! Start bash from cli callback
 * XXX Application specific??
 * XXX replace fprintf with clicon_err?
 */ 
int
cli_start_shell(clicon_handle h, 
		cvec         *vars, 
		cvec         *argv)
{
    char          *cmd;
    struct passwd *pw;
    int            retval = -1;
    char           bcmd[128];
    cg_var        *cv1 = cvec_i(vars, 1);

    cmd = (cvec_len(vars)>1 ? cv_string_get(cv1) : NULL);

    if ((pw = getpwuid(getuid())) == NULL){
	fprintf(stderr, "%s: getpwuid: %s\n", 
               __FUNCTION__, strerror(errno));
	goto done;
    }
    if (chdir(pw->pw_dir) < 0){
	fprintf(stderr, "%s: chdir(%s): %s\n",
		__FUNCTION__, pw->pw_dir, strerror(errno));
	endpwent();
	goto done;
    }
    endpwent();
    cli_signal_flush(h);
    cli_signal_unblock(h);
    if (cmd){
	snprintf(bcmd, 128, "bash -l -c \"%s\"", cmd);
	if (system(bcmd) < 0){
	    cli_signal_block(h);
	    fprintf(stderr, "%s: system(bash -c): %s\n", 
		    __FUNCTION__, strerror(errno));
	    goto done;
	}
    }
    else
	if (system("bash -l") < 0){
	    cli_signal_block(h);
	    fprintf(stderr, "%s: system(bash): %s\n", 
		    __FUNCTION__, strerror(errno));
	    goto done;
	}
    cli_signal_block(h);
#if 0 /* Allow errcodes from bash */
    if (retval != 0){
	fprintf(stderr, "%s: system(%s) code=%d\n", __FUNCTION__, cmd, retval);
	goto done;
    }
#endif
    retval = 0;
 done:
    return retval;
}
Esempio n. 7
0
/*! Call expand callback and insert expanded commands in place of variable
 * variable argument callback variant
 * @see pt_expand_fn
 */
static int
pt_expand_fnv(cligen_handle h, 
	      cg_obj       *co,     
	      cvec         *cvv,
	      parse_tree   *ptn,
	      cg_obj       *parent)
{
    int     retval = -1;
    cvec   *commands = cvec_new(0);
    cvec   *helptexts = cvec_new(0);
    cg_var *cv = NULL;
    char   *helpstr;
    cg_obj *con;
    int     i;

    if ((*co->co_expandv_fn)(
			     cligen_userhandle(h)?cligen_userhandle(h):h, 
			     co->co_expand_fn_str, 
			     cvv,
			     co->co_expand_fn_vec,
			     commands, 
			     helptexts) < 0)
	goto done;
    i = 0;
    while ((cv = cvec_each(commands, cv)) != NULL) {
	if (i < cvec_len(helptexts)){
	    helpstr = strdup(cv_string_get(cvec_i(helptexts, i)));
	}
	else
	    helpstr = NULL;
	i++;
	pt_realloc(ptn);
	if (co_expand_sub(co, parent, 
			  &ptn->pt_vec[ptn->pt_len-1]) < 0)
	    goto done;
	con = ptn->pt_vec[ptn->pt_len-1];
	if (transform_var_to_cmd(con, 
				 strdup(cv_string_get(cv)),
				 helpstr) < 0)
	    goto done;
    }
    if (commands)
	cvec_free(commands);
    if (helptexts)
	cvec_free(helptexts);
    retval = 0;
 done:
    return retval;

}
Esempio n. 8
0
/*! Append a new cligen variable (cv) to cligen variable vector (cvec) and return it.
 *
 * @param[in] cvv   Cligen variable vector
 * @param[in] type  Append a new cv to the vector with this type
 * @see also cv_new, but this is allocated contiguosly as a part of a cvec.
 */
cg_var *
cvec_add(cvec        *cvv, 
	 enum cv_type type)
{
    int     len = cvv->vr_len + 1;
    cg_var *cv;

    if ((cvv->vr_vec = realloc(cvv->vr_vec, len*sizeof(cg_var))) == NULL)
	return NULL;
    cvv->vr_len = len;
    cv = cvec_i(cvv, len-1);
    memset(cv, 0, sizeof(*cv));
    cv->var_type = type;
    return cv;
}
Esempio n. 9
0
/*! Set syntax mode
 */
int
cli_set_mode(clicon_handle h, 
	      cvec         *vars, 
	      cvec         *argv)
{
    int     retval = -1;
    char   *str = NULL;

    if (cvec_len(argv) != 1){
	clicon_err(OE_PLUGIN, 0, "Requires one element to be cli mode");
	goto done;
    }
    str = cv_string_get(cvec_i(argv, 0));
    cli_set_syntax_mode(h, str);
    retval = 0;
  done:
    return retval;
}
Esempio n. 10
0
/*! Unlock database
 * 
 * @param[in] h      Clicon handle
 * @param[in] cvv    Not used
 * @param[in] arg    A string with <database> 
 * @code
 * lock("comment"), cli_lock("running"); 
 * @endcode
 * XXX: format is a memory leak
 */
int
cli_unlock(clicon_handle h, 
	   cvec         *cvv, 
	   cvec         *argv)
{
    char            *db;
    int              retval = -1;

    if (cvec_len(argv) != 1){
	clicon_err(OE_PLUGIN, 0, "Requires arguments: <db>");
	goto done;
    }
    db = cv_string_get(cvec_i(argv, 0));
    if (clicon_rpc_unlock(h, db) < 0) 
	goto done;
    retval = 0;
  done:
    return retval;
}
Esempio n. 11
0
/*! Compare two dbs using XML. Write to file and run diff
 * @param[in]   h     Clicon handle
 * @param[in]   cvv  
 * @param[in]   arg   arg: 0 as xml, 1: as text
 */
int
compare_dbs(clicon_handle h, 
	    cvec         *cvv, 
	    cvec         *argv)
{
    cxobj *xc1 = NULL; /* running xml */
    cxobj *xc2 = NULL; /* candidate xml */
    cxobj *xerr;
    int    retval = -1;
    int    astext;

    if (cvec_len(argv) > 1){
	clicon_err(OE_PLUGIN, 0, "Requires 0 or 1 element. If given: astext flag 0|1");
	goto done;
    }
    if (cvec_len(argv))
	astext = cv_int32_get(cvec_i(argv, 0));
    else
	astext = 0;
    if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0)
	goto done;
    if ((xerr = xpath_first(xc1, "/rpc-error")) != NULL){
	clicon_rpc_generate_error("Get configuration", xerr);
	goto done;
    }
    if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0)
	goto done;
    if ((xerr = xpath_first(xc2, "/rpc-error")) != NULL){
	clicon_rpc_generate_error("Get configuration", xerr);
	goto done;
    }
    if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */
	goto done;
    retval = 0;
  done:
    if (xc1)
	xml_free(xc1);    
    if (xc2)
	xml_free(xc2);

    return retval;
}
Esempio n. 12
0
/*! Set debug level on backend daemon (not CLI)
 * @param[in] h     Clicon handle
 * @param[in] vars  If variable "level" exists, its integer value is used
 * @param[in] arg   Else use the integer value of argument
 * @note The level is either what is specified in arg as int argument.
 *       _or_ if a 'level' variable is present in vars use that value instead.
 */
int
cli_debug_backend(clicon_handle h, 
		  cvec         *vars, 
		  cvec         *argv)
{
    int     retval = -1;
    cg_var *cv;
    int     level;

    if ((cv = cvec_find(vars, "level")) == NULL){
	if (cvec_len(argv) != 1){
	    clicon_err(OE_PLUGIN, 0, "Requires either label var or single arg: 0|1");
	    goto done;
	}
	cv = cvec_i(argv, 0);
    }
    level = cv_int32_get(cv);
    /* config daemon */
    retval = clicon_rpc_debug(h, level);
 done:
    return retval;
}
Esempio n. 13
0
/*! Set debug level on CLI client (not backend daemon)
 * @param[in] h     Clicon handle
 * @param[in] vars  If variable "level" exists, its integer value is used
 * @param[in] arg   Else use the integer value of argument
 * @note The level is either what is specified in arg as int argument.
 *       _or_ if a 'level' variable is present in vars use that value instead.
 */
int
cli_debug_cli(clicon_handle h, 
	       cvec         *vars, 
	       cvec         *argv)
{
    int     retval = -1;
    cg_var *cv;
    int     level;

    if ((cv = cvec_find(vars, "level")) == NULL){
	if (cvec_len(argv) != 1){
	    clicon_err(OE_PLUGIN, 0, "Requires either label var or single arg: 0|1");
	    goto done;
	}
	cv = cvec_i(argv, 0);
    }
    level = cv_int32_get(cv);
    /* cli */
    clicon_debug_init(level, NULL); /* 0: dont debug, 1:debug */
    retval = 0;
 done:
    return retval;
}
Esempio n. 14
0
/*
 * match_pattern_node
 * Non-terminal. Need to match exact.
 * INPUT:
 *   h         CLIgen handle
 *   string0   Input string to match
 *   pt        Vector of commands (array of cligen object pointers (cg_obj)
 *   pt_max    Length of the pt array
 *   level     How many levels (words) into string0
 *   use_pref  Set this flag value if you want to use the preferences between
 *             matches. It is only when you want a final exact match (not 
 *             completion or show options) that you should set this.
 * RETURNS:
 *   The number of matches (0-n) in pt or -1 on error. See matchlen below.
 * OUTPUT:
 *   ptp       Returns the vector at the place of matching
 *   matchv    A vector of integers containing which 
 *   matchlen  Length of matchv. That is, # of matches and same as return 
 *              value (if 0-n)
 *   cvec      cligen variable vector containing vars/values pair for completion
 *   reason0   If retval = 0, this may be malloced to indicate reason for not
 *             matching variables, if given. Need to be free:d
 */
static int 
match_pattern_node(cligen_handle h, 
		   char *string0, 
		   parse_tree pt,
		   int level, 
		   int use_pref, 
		   int hide,
		   pt_vec *ptp, 
		   int *matchv[], 
		   int *matchlen,
		   cvec  *cvec,
		   char **reason0
		   )
{
    char *string = NULL;
    int i;
    int match;
    int matches = 0;
    int perfect = 0;
    int retval = -1;
    cg_obj *co, *co_match;
    cg_obj *co_orig;
    int rest_match = -1;
    int cmd_levels;
    int p;
    int preference = 0;
    int exact;
    char *reason;
    int findreason;
    parse_tree ptn={0,};     /* Expanded */
    cg_var *cv = NULL;

    co_match = NULL;
    if (level > command_levels(string0)){
	fprintf(stderr, "%s: level > command_level in %s\n",
		__FUNCTION__, string0);
	return -1;
    }
    /* If there are only variables in the list, then keep track of variable match errors */
    findreason = 0;
    if (reason0)
	for (i=0; i<pt.pt_len; i++){ 
	    if ((co = pt.pt_vec[i]) == NULL)
		continue;
	    if (co->co_type != CO_VARIABLE){
		findreason = 0;
		break;
	    }
	    findreason++;
	}
    extract_substring(string0, level, &string);
    for (i=0; i<pt.pt_len; i++){
	if ((co = pt.pt_vec[i]) == NULL)
	    continue;
	reason = NULL;
	if ((match = match_object(string, co, &exact, findreason?&reason:NULL)) < 0)
	    goto error;
	if (match){
	    assert(reason==NULL);
	    /* Special case to catch rest variable and space delimited
	       arguments after it */
	    if (co->co_type == CO_VARIABLE && co->co_vtype == CGV_REST)
		rest_match = i;
	    if (match_perfect(string, co)){
		if (!perfect){
		    matches = 0;
		    perfect = 1;
		}
	    }
	    else{
		if (perfect)
		    break;
 		if (1 || use_pref){
		    p = co_pref(co, exact);
		    if (p < preference)
			continue; /* ignore */
		    if (p > preference){
			preference = p;
			matches = 0; /* Start again at this level */
		    }
		}
	    }
	    co_match = co;
	    matches++;
	}
	/* match == 0, co type is variable and findreason, then reason is set 
	   this may not be the best preference, we just set the first
	*/
	if (reason){
	    if (*reason0 == NULL)
		*reason0 = reason;
	    reason = NULL;
	    findreason = 0;
	}
    } /* for */
    if (matches != 0 && reason0 && *reason0){
	    free(*reason0);
	    *reason0 = NULL;
	}

    if (matches != 1) {
#ifdef notneeded
	if (matches == 0){
	    cligen_nomatch_set(h, "Unrecognized command");
	}
	else
	    cligen_nomatch_set(h, "Ambigious command");
#endif
	retval = 0;
	goto quit;
    }
    assert(co_match);
    if ((cmd_levels = command_levels(string0)) < 0)
	goto error;

    /* co_orig is original object in case of expansion */
    co_orig = co_match->co_ref?co_match->co_ref: co_match;
    if (pt_expand_1(h, co_match, &co_match->co_pt) < 0) /* sub-tree expansion */
	goto error; 

    if (co_match->co_type == CO_VARIABLE){
	if ((cv = add_cov_to_cvec(co_match, string, cvec)) == NULL)
	    goto error;
    }
    else
	if (co_match->co_type == CO_COMMAND && co_orig->co_type == CO_VARIABLE)
	    if ((cv = add_cov_to_cvec(co_orig, string, cvec)) == NULL)
		goto error;
    if (pt_expand_2(h, &co_match->co_pt, cvec, &ptn, hide) < 0) /* expand/choice variables */
	goto error;
    if (level+1 == cmd_levels)
	retval = match_pattern_terminal(h,
					string0, ptn, 
					level+1, use_pref,
					ptp, matchv, matchlen, reason0);
    else
	retval = match_pattern_node(h, 
				    string0, ptn,
				    level+1, use_pref, hide,
				    ptp, matchv, matchlen, cvec, reason0);

    if (pt_expand_add(co_orig, ptn) < 0) /* add expanded ptn to orig parsetree */
	goto error;
    if (co_match->co_type == CO_COMMAND && co_orig->co_type == CO_VARIABLE)
	if (co_value_set(co_orig, co_match->co_command) < 0)
	    goto error;


    /* Cleanup made on top-level */
    
    /* 
     * Special case: we have matched a REST variable (anything) and
     * there is more text have this word, then we can match REST
     */
    if (retval == 0 && rest_match != -1){
	retval = 1;
	if (*matchlen < 1){
	    *matchlen = 1;
	    if ((*matchv = realloc(*matchv, (*matchlen)*sizeof(int))) == NULL){
		fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
		return -1;
	    }
	}
	else
	    *matchlen = 1;
	*ptp = pt.pt_vec;
	(*matchv)[0] = rest_match;
    }
  quit:
    if (cv){ /* cv may be stale */
	cv = cvec_i(cvec, cvec_len(cvec)-1);
	cv_reset(cv);
	cvec_del(cvec, cv);
    }
    /* Only the last level may have multiple matches */
    if (string)
	free(string);
    return retval;
  error:
    retval = -1;
    goto quit;
}
Esempio n. 15
0
/*! Copy one configuration object to antother
 *
 * Works for objects that are items ina yang list with a keyname, eg as:
 *   list sender{ 
 *      key name;	
 *	leaf name{...
 *
 * @param[in]  h    CLICON handle
 * @param[in]  cvv  Vector of variables from CLIgen command-line
 * @param[in]  argv Vector: <db>, <xpath>, <field>, <fromvar>, <tovar>
 * Explanation of argv fields:
 *  db:     Database name, eg candidate|tmp|startup
 *  xpath:  XPATH expression with exactly two %s pointing to field and from name
 *  field:  Name of list key, eg name
 *  fromvar:Name of variable containing name of object to copy from (given by xpath)
 *  tovar:  Name of variable containing name of object to copy to.
 * @code
 * cli spec:
 *  copy snd <n1:string> to <n2:string>, cli_copy_config("candidate", "/sender[%s='%s']", "from", "n1", "n2");
 * cli command:
 *  copy snd from to to
 * @endcode
 */
int
cli_copy_config(clicon_handle h, 
		cvec         *cvv, 
		cvec         *argv)
{
    int          retval = -1;
    char        *db;
    cxobj       *x1 = NULL; 
    cxobj       *x2 = NULL; 
    cxobj       *x;
    char        *xpath;
    int          i;
    int          j;
    cbuf        *cb = NULL;
    char        *keyname;
    char        *fromvar;
    cg_var      *fromcv;
    char        *fromname = NULL;
    char        *tovar;
    cg_var      *tocv;
    char        *toname;
    cxobj       *xerr;

    if (cvec_len(argv) != 5){
	clicon_err(OE_PLUGIN, 0, "Requires four elements: <db> <xpath> <keyname> <from> <to>");
	goto done;
    }
    /* First argv argument: Database */
    db = cv_string_get(cvec_i(argv, 0));
    /* Second argv argument: xpath */
    xpath = cv_string_get(cvec_i(argv, 1));
    /* Third argv argument: name of keyname */
    keyname = cv_string_get(cvec_i(argv, 2));
    /* Fourth argv argument: from variable */
    fromvar = cv_string_get(cvec_i(argv, 3));
    /* Fifth argv argument: to variable */
    tovar = cv_string_get(cvec_i(argv, 4));
    
    /* Get from variable -> cv -> from name */
    if ((fromcv = cvec_find(cvv, fromvar)) == NULL){
	clicon_err(OE_PLUGIN, 0, "fromvar '%s' not found in cligen var list", fromvar);	
	goto done;
    }
    /* Get from name from cv */
    fromname = cv_string_get(fromcv);
    /* Create xpath */
    if ((cb = cbuf_new()) == NULL){
	clicon_err(OE_PLUGIN, errno, "cbuf_new");	
	goto done;
    }
    /* Sanity check that xpath contains exactly two %s, ie [%s='%s'] */
    j = 0;
    for (i=0; i<strlen(xpath); i++){
	if (xpath[i] == '%')
	    j++;
    }
    if (j != 2){
	clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have two '%%'", xpath);	
	goto done;
    }
    cprintf(cb, xpath, keyname, fromname);	
    /* Get from object configuration and store in x1 */
    if (clicon_rpc_get_config(h, db, cbuf_get(cb), &x1) < 0)
	goto done;
    if ((xerr = xpath_first(x1, "/rpc-error")) != NULL){
	clicon_rpc_generate_error("Get configuration", xerr);
	goto done;
    }

    /* Get to variable -> cv -> to name */
    if ((tocv = cvec_find(cvv, tovar)) == NULL){
	clicon_err(OE_PLUGIN, 0, "tovar '%s' not found in cligen var list", tovar);
	goto done;
    }
    toname = cv_string_get(tocv);
    /* Create copy xml tree x2 */
    if ((x2 = xml_new("new", NULL, NULL)) == NULL)
	goto done;
    if (xml_copy(x1, x2) < 0)
	goto done;
    xml_name_set(x2, "config");
    cprintf(cb, "/%s", keyname);	
    if ((x = xpath_first(x2, "%s", cbuf_get(cb))) == NULL){
	clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname);
	goto done;
    }
    x = xml_find(x, "body");
    xml_value_set(x, toname);
    /* resuse cb */
    cbuf_reset(cb);
    /* create xml copy tree and merge it with database configuration */
    clicon_xml2cbuf(cb, x2, 0, 0);
    if (clicon_rpc_edit_config(h, db, OP_MERGE, cbuf_get(cb)) < 0)
	goto done;
    retval = 0;
 done:
    if (cb)
	cbuf_free(cb);
    if (x1 != NULL)
	xml_free(x1);
    if (x2 != NULL)
	xml_free(x2);
    return retval;
}
Esempio n. 16
0
/*! Modify xml datastore from a callback using xml key format strings
 * @param[in]  h    Clicon handle
 * @param[in]  cvv  Vector of cli string and instantiated variables 
 * @param[in]  argv Vector. First element xml key format string, eg "/aaa/%s"
 * @param[in]  op   Operation to perform on database
 * Cvv will contain first the complete cli string, and then a set of optional
 * instantiated variables.
 * Example:
 * cvv[0]  = "set interfaces interface eth0 type bgp"
 * cvv[1]  = "eth0"
 * cvv[2]  = "bgp"
 * argv[0] = "/interfaces/interface/%s/type"
 * op: OP_MERGE
 * @see cli_callback_generate where arg is generated
 */
static int
cli_dbxml(clicon_handle       h, 
	  cvec               *cvv, 
	  cvec               *argv, 
	  enum operation_type op)
{
    int        retval = -1;
    char      *str = NULL;
    char      *api_path_fmt;  /* xml key format */
    char      *api_path = NULL; /* xml key */
    cg_var    *cval;
    int        len;
    cg_var    *arg;
    cbuf      *cb = NULL;
    yang_stmt *yspec;
    cxobj     *xbot = NULL; /* xpath, NULL if datastore */
    yang_stmt *y = NULL; /* yang spec of xpath */
    cxobj     *xtop = NULL; /* xpath root */
    cxobj     *xa;           /* attribute */
    cxobj     *xb;           /* body */

    if (cvec_len(argv) != 1){
	clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format string");
	goto done;
    }
    if ((yspec = clicon_dbspec_yang(h)) == NULL){
	clicon_err(OE_FATAL, 0, "No DB_SPEC");
	goto done;
    }
    arg = cvec_i(argv, 0);
    api_path_fmt = cv_string_get(arg);
    if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
	goto done;
    /* Create config top-of-tree */
    if ((xtop = xml_new("config", NULL, NULL)) == NULL)
	goto done;
    xbot = xtop;
    if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y) < 1)
	goto done; 
    if ((xa = xml_new("operation", xbot, NULL)) == NULL)
	goto done;
    xml_type_set(xa, CX_ATTR);
    if (xml_value_set(xa,  xml_operation2str(op)) < 0)
	goto done;
    if (yang_keyword_get(y) != Y_LIST && yang_keyword_get(y) != Y_LEAF_LIST){
	len = cvec_len(cvv);
	if (len > 1){
	    cval = cvec_i(cvv, len-1); 
	    if ((str = cv2str_dup(cval)) == NULL){
		clicon_err(OE_UNIX, errno, "cv2str_dup");
		goto done;
	    }
	    if ((xb = xml_new("body", xbot, NULL)) == NULL)
		goto done; 
	    xml_type_set(xb, CX_BODY);
	    if (xml_value_set(xb,  str) < 0)
		goto done;
	}
    }
    if ((cb = cbuf_new()) == NULL){
	clicon_err(OE_XML, errno, "cbuf_new");
	goto done;
    }
    if (clicon_xml2cbuf(cb, xtop, 0, 0) < 0)
	goto done;
    if (clicon_rpc_edit_config(h, "candidate", OP_NONE, cbuf_get(cb)) < 0)
	goto done;
    if (clicon_autocommit(h)) {
	if (clicon_rpc_commit(h) < 0) 
	    goto done;
    }
    retval = 0;
 done:
    if (cb)
	cbuf_free(cb);
    if (str)
	free(str);
    if (api_path)
	free(api_path);  
    if (xtop)
	xml_free(xtop);
    return retval;
}
Esempio n. 17
0
void handle_macro(preprocessor_state* preproc, int macro)
{
	FILE* input = preproc->input;
	FILE* output = preproc->output;
	lexer_state* lexer = &preproc->lexer;

	rsw_cstr expansion;
	init_cstr_str(&expansion, preproc->values.a[macro], strlen(preproc->values.a[macro]));

	macro_params* p = GET_PARAM(&preproc->params, macro);
	
	//do initial argument expansion
	if (p->num_params >= 0)
		parse_params(preproc, macro, &expansion);

	cvector_i valid_macros;
	cvec_i(&valid_macros, preproc->macros.size, preproc->macros.size);
	for (int i=0; i < preproc->macros.size; ++i) {
		valid_macros.a[i] = i;
	}

	rescan_expansion(preproc, &expansion, &valid_macros, macro);

	lexer_state save_lex;
	long fpos;
	token_lex t;

	save_lex = *lexer;
	if ((fpos = ftell(input)) == -1) {
		perror("ftell error in handle_macro");
		exit(0);
	}
	t = read_token(input, lexer, NULL);
	*lexer = save_lex;
	if (fseek(input, fpos, SEEK_SET)) {
		perror("fseek failure in handle_macro");
		exit(0);
	}
	
	if (t.tok.type == LPAREN) { 
		char* search, *found;
		search = expansion.a;

		int len;
		for (int i=0; i < preproc->macros.size; ++i) {
			if (i == macro)
				continue;

			len = strlen(preproc->macros.a[i]);
			search = expansion.a;
			while (1) {
				found = strstr(search, preproc->macros.a[i]);
				if (!found)
					break;
				if (found[len]) {
					search = found + len;
					continue;
				}

				if (found != expansion.a) {
					if (isalpha(found[-1]) || found[-1] == '_') {
						search = found + len;
						continue;
					}
				}
				//don't need to check the end because we already did
				
				p = GET_PARAM(&preproc->params, i);
				if (p->num_params != -1) {
					*found = 0; //cut off matched macro
					fprintf(output, "%s", expansion.a);
					handle_macro(preproc, i);

					goto exit;
				}

				search = found + len;
			}
		}
	} else if (t.tok.type == ID || t.tok.type == STR_LITERAL) {
		free(t.tok.v.id);
	}

	fprintf(output, "%s", expansion.a);

exit:

	free_cstr(&expansion);
	cvec_free_i(&valid_macros);
}
Esempio n. 18
0
void prescan_argument(preprocessor_state* preproc, rsw_cstr* expansion)
{
	int loc, macro_name_len, macro_val_len;
	char* ptr, *search, *found;
	macro_params* p;

	/*
 	 * alternative method, actually parse tokens from the string
 	 * seems like overkill for now but if I run into some issue that makes it
 	 * more reasonable I'll consider it
 	 *
	vector_token_lex tlex;
	vec_token_lex(&tlex, 0, 20, free_token_lex, NULL);
	token_lex tok_lex;

	lexer_state lex = { 1, 0, 0, 0};
	tok_lex = read_token_from_str(&expansion->a[lex.cur_char], &lex, NULL);
	while (tok_lex.tok.type != END) {
		cvec_push_token_lex(&tlek, &tok_lex);
		tok_lex = read_token_from_str(&expansion->a[lex.cur_char], &lex, NULL);
	}

	for (int i=0; i < tlex.size; ++i ) {
		if (tlex.a[i].tok.type == ID) {
			for (int j=0; j < preproc->macros.size; ++j) {
				if (!strcmp(tlex.a[i].tok.v.id, preproc->values.a[j])) {
					p = GET_PARAM(&preproc->params, j);
					if (p->num_params != -1) {
						if (tlex.a[i+1] != LPAREN) 
							continue;
						loc = tlex.a[i].char_pos - expansion->a;
						macro_expansion(preproc, loc, NULL, j);
					} else {
						lex = { 1, 0, 0, tlex.a[i].char_pos };

						tok_lex = read_token(&preproc->values[j][lex.cur_char], &lex, NULL);
						while (tok_lex.tok.type != END) {

						}
					}
				}
			}
		}
	}
	*/

	cvector_i valid_macros;
	cvec_i(&valid_macros, preproc->macros.size, preproc->macros.size);

	for (int j=0; j < preproc->macros.size; ++j) {
			valid_macros.a[j] = j;
	}

	for (int i=0; i < preproc->macros.size; ++i) {
		search = expansion->a;
		p = GET_PARAM(&preproc->params, i);

		macro_name_len = strlen(preproc->macros.a[i]);
		macro_val_len = strlen(preproc->values.a[i]);

		while (1) {
			found = strstr(search, preproc->macros.a[i]);
			if (!found)
				break;

			//since we should be working in tokens not characters
			//I have to check that the result is not inside another identifier
			//check before and after TODO I think these tests are insufficient
			if (found != expansion->a) {
				if (isalpha(found[-1]) || found[-1] == '_') {
					search = found + macro_name_len;
					continue;
				}
			}

			if (isalnum(found[macro_name_len]) || found[macro_name_len] == '_') {
				search = found + macro_name_len;
				continue;
			}

			if (p->num_params >= 0) {
				ptr = found + macro_name_len;
				while (isspace(*ptr)) ++ptr;
				if (*ptr != '(') {
					search = found + macro_name_len;
					continue;
				}
			}

			loc = found - expansion->a;
			loc = macro_expansion(preproc, expansion, loc, &valid_macros, i);
			search = &expansion->a[loc];
		}
	}

	cvec_free_i(&valid_macros);
}
Esempio n. 19
0
/*! Copy database to local file 
 * Utility function used by cligen spec file
 * @param[in] h     CLICON handle
 * @param[in] cvv  variable vector (containing <varname>)
 * @param[in] argv  a string: "<dbname> <varname>" 
 *   <dbname>  is running, candidate, or startup
 *   <varname> is name of cligen variable in the "cvv" vector containing file name
 * Note that "filename" is local on client filesystem not backend.
 * The function can run without a local database
 * @note The file is saved with dummy top-tag: clicon: <clicon></clicon>
 * @code
 *   save file <name:string>, save_config_file("running name");
 * @endcode
 * @see load_config_file
 */
int
save_config_file(clicon_handle h, 
		 cvec         *cvv, 
		 cvec         *argv)
{
    int        retval = -1;
    char      *filename = NULL;
    cg_var    *cv;
    char      *dbstr;
    char      *varstr;
    cxobj     *xt = NULL;
    cxobj     *xerr;
    FILE      *f = NULL;

    if (cvec_len(argv) != 2){
	if (cvec_len(argv)==1)
	    clicon_err(OE_PLUGIN, 0, "Got single argument:\"%s\". Expected \"<dbname>,<varname>\"",
		       cv_string_get(cvec_i(argv,0)));
	else
	    clicon_err(OE_PLUGIN, 0, " Got %d arguments. Expected: <dbname>,<varname>",
		       cvec_len(argv));

	goto done;
    }
    dbstr = cv_string_get(cvec_i(argv, 0));
    varstr  = cv_string_get(cvec_i(argv, 1));
    if (strcmp(dbstr, "running") != 0 && 
	strcmp(dbstr, "candidate") != 0 &&
	strcmp(dbstr, "startup") != 0) {
	clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);	
	goto done;
    }
    if ((cv = cvec_find(cvv, varstr)) == NULL){
	clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr);	
	goto done;
    }
    filename = cv_string_get(cv);
    if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0)
	goto done;
    if (xt == NULL){
	clicon_err(OE_CFG, 0, "get config: empty tree"); /* Shouldnt happen */
	goto done;
    }
    if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
	clicon_rpc_generate_error("Get configuration", xerr);
	goto done;
    }
    /* get-config returns a <data> tree. Save as <config> tree so it can be used
     * as data-store.
     */
    if (xml_name_set(xt, "config") < 0)
	goto done;
    if ((f = fopen(filename, "w")) == NULL){
	clicon_err(OE_CFG, errno, "Creating file %s", filename);
	goto done;
    } 
    if (clicon_xml2file(f, xt, 0, 1) < 0)
	goto done;
    retval = 0;
    /* Fall through */
  done:
    if (xt)
	xml_free(xt);
    if (f != NULL)
	fclose(f);
    return retval;
}
Esempio n. 20
0
/*! Load a configuration file to candidate database
 * Utility function used by cligen spec file
 * @param[in] h     CLICON handle
 * @param[in] cvv   Vector of variables (where <varname> is found)
 * @param[in] argv  A string: "<varname> (merge|replace)" 
 *   <varname> is name of a variable occuring in "cvv" containing filename
 * @note that "filename" is local on client filesystem not backend. 
 * @note file is assumed to have a dummy top-tag, eg <clicon></clicon>
 * @code
 *   # cligen spec
 *   load file <name2:string>, load_config_file("name2","merge");
 * @endcode
 * @see save_config_file
 */
int 
load_config_file(clicon_handle h, 
		 cvec         *cvv, 
		 cvec         *argv)
{
    int         ret = -1;
    struct stat st;
    char       *filename = NULL;
    int         replace;
    cg_var     *cv;
    char       *opstr;
    char       *varstr;
    int         fd = -1;
    cxobj      *xt = NULL;
    cxobj      *x;
    cbuf       *cbxml;

    if (cvec_len(argv) != 2){
	if (cvec_len(argv)==1)
	    clicon_err(OE_PLUGIN, 0, "Got single argument:\"%s\". Expected \"<varname>,<op>\"", cv_string_get(cvec_i(argv,0)));
	else
	    clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <varname>,<op>", cvec_len(argv));
	goto done;
    }
    varstr = cv_string_get(cvec_i(argv, 0));
    opstr  = cv_string_get(cvec_i(argv, 1));
    if (strcmp(opstr, "merge") == 0) 
	replace = 0;
    else if (strcmp(opstr, "replace") == 0) 
	replace = 1;
    else{
	clicon_err(OE_PLUGIN, 0, "No such op: %s, expected merge or replace", opstr);	
	goto done;
    }
    if ((cv = cvec_find(cvv, varstr)) == NULL){
	clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr);	
	goto done;
    }
    filename = cv_string_get(cv);
    if (stat(filename, &st) < 0){
 	clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s", 
		   filename, strerror(errno));
	goto done;
    }
    /* Open and parse local file into xml */
    if ((fd = open(filename, O_RDONLY)) < 0){
	clicon_err(OE_UNIX, errno, "open(%s)", filename);
	goto done;
    }
    if (xml_parse_file(fd, "</clicon>", NULL, &xt) < 0)
	goto done;
    if (xt == NULL)
	goto done;
    if ((cbxml = cbuf_new()) == NULL)
	goto done;
    x = NULL;
    while ((x = xml_child_each(xt, x, -1)) != NULL) {
	/* Ensure top-level is "config", maybe this is too rough? */
	xml_name_set(x, "config");
	if (clicon_xml2cbuf(cbxml, x, 0, 0) < 0)
	    goto done;
    }
    if (clicon_rpc_edit_config(h, "candidate",
			       replace?OP_REPLACE:OP_MERGE, 
			       cbuf_get(cbxml)) < 0)
	goto done;
    cbuf_free(cbxml);
    //    }
    ret = 0;
 done:
    if (xt)
	xml_free(xt);
    if (fd != -1)
	close(fd);
    return ret;
}