/* * An example of a callback handling a complex syntax */ int letters(cligen_handle h, cvec *vars, cg_var *arg) { char *str; cg_var *cv; if ((str = cvec_find_str(vars, "ca")) != NULL) printf("%s\n", str); if ((cv = cvec_find(vars, "int")) != NULL) printf("%d\n", cv_int32_get(cv)); if ((str = cvec_find_str(vars, "cb")) != NULL) printf("%s\n", str); if ((str = cvec_find_str(vars, "dd")) != NULL) printf("%s\n", str); if ((str = cvec_find_str(vars, "ee")) != NULL) printf("%s\n", str); return 0; }
/*! set debug level on stderr (not syslog). * 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. * XXX obsolete. Use cli_debug_cliv or cli_debug_backendv instead */ int cli_debug(clicon_handle h, cvec *vars, cg_var *arg) { cg_var *cv; int level; if ((cv = cvec_find(vars, "level")) == NULL) cv = arg; level = cv_int32_get(cv); /* cli */ clicon_debug_init(level, NULL); /* 0: dont debug, 1:debug */ /* config daemon */ if (clicon_rpc_debug(h, level) < 0) goto done; done: return 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; }
/*! 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; }
/*! 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; }
/*! 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; }
/*! 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; }
/* * generic_validate * * key values are checked for validity independent of user-defined callbacks * They are checked as follows: * 1. If no value and default value defined, add it. * 2. If no value and mandatory flag set in spec, report error. * 3. Validate value versus spec, and report error if no match. Currently only int ranges and * string regexp checked. */ static int generic_validate(clicon_handle h, char *dbname, const struct dbdiff *dd) { int i, j; char *key; cvec *cvec = NULL; cg_var *cv; cg_varspec *cs; int retval = -1; cg_obj *co; cg_obj *cov; parse_tree *dbspec_co; char *reason = NULL; parse_tree *pt; if ((dbspec_co = clicon_dbspec_pt(h)) == NULL) goto done; /* dd->df_ents[].dfe_key1 (running), dd->df_ents[].dfe_key2 (candidate) */ for (i = 0; i < dd->df_nr; i++) { if ((key = dd->df_ents[i].dfe_key2) == NULL) continue; if ((co = key2spec_co(dbspec_co, key)) == NULL) continue; /* read variable list from db */ if ((cvec = dbkey2cvec(dbname, key)) == NULL) goto done; /* Loop over all co:s children (spec) and check if actual values in db(cv) satisfies them */ pt = &co->co_pt; for (j=0; j<pt->pt_len; j++){ if ((cov = pt->pt_vec[j]) == NULL) continue; if (cov->co_type == CO_VARIABLE){ /* There is no db-value, but dbspec has default value */ if ((cv = dbspec_default_get(cov)) != NULL && cvec_find(cvec, cov->co_command) == NULL){ cv_flag_set(cv, V_DEFAULT); /* mark it as default XXX not survive DB */ /* add default value to cvec */ if (cvec_add_cv(cvec, cv) < 0){ clicon_err(OE_CFG, 0, "cvec_add_cv"); goto done; } /* Write to database */ if (cvec2dbkey(dbname, key, cvec) < 0) goto done; } else if (!dbspec_optional_get(cov) && cvec_find(cvec, cov->co_command) == NULL){ clicon_err(OE_CFG, 0, "key %s: Missing mandatory variable: %s\n", key, cov->co_command); goto done; } } } cv = NULL; /* Loop over all actual db/cv:s och check their validity */ while ((cv = cvec_each(cvec, cv))) { if ((cov = co_find_one(*pt, cv_name_get(cv))) == NULL){ clicon_err(OE_CFG, 0, "key %s: variable %s not found in co-spec", key, cv_name_get(cv)); goto done; } if ((cs = co2varspec(cov)) == NULL) continue; if (cv_validate(cv, cs, &reason) != 1){ /* We ignore errors */ clicon_err(OE_DB, 0, "key %s: validation of %s failed\n", key, cov->co_command); goto done; } } if (cvec){ cvec_free(cvec); cvec = NULL; } } /* for */ retval = 0; done: if (cvec) cvec_free(cvec); if (reason) free(reason); return retval; }