/* * ***** BEGIN LICENSE BLOCK ***** Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren This file is part of CLIXON. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Alternatively, the contents of this file may be used under the terms of the GNU General Public License Version 3 or later (the "GPL"), in which case the provisions of the GPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the GPL, and not to allow others to use your version of this file under the terms of Apache License version 2, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the Apache License version 2 or the GPL. ***** END LICENSE BLOCK ***** * */ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_CRYPT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include /* cligen */ #include /* clicon */ #include /* Exported functions in this file are in clixon_cli_api.h */ #include "clixon_cli_api.h" static int xml2csv(FILE *f, cxobj *x, cvec *cvv); //static int xml2csv_raw(FILE *f, cxobj *x); /*! Completion callback intended for automatically generated data model * * Returns an expand-type list of commands as used by cligen 'expand' * functionality. * * Assume callback given in a cligen spec: a " * @param[out] len len of return commands & helptxt * @param[out] commands vector of function pointers to callback functions * @param[out] helptxt vector of pointers to helptexts * @see cli_expand_var_generate This is where arg is generated */ int expand_dbvar(void *h, char *name, cvec *cvv, cg_var *arg, int *nr, char ***commands, char ***helptexts) { int nvec; char **vec = NULL; int retval = -1; char *xkfmt; char *str; char *dbstr; cxobj *xt = NULL; char *xkpath = NULL; cxobj **xvec = NULL; size_t xlen = 0; cxobj *x; char *bodystr; int i; int j; int k; int i0; if (arg == NULL || (str = cv_string_get(arg)) == NULL){ clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); goto done; } /* In the example, str = "candidate /x/m1/%s/b" */ if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); goto done; } dbstr = vec[0]; if (strcmp(dbstr, "running") != 0 && strcmp(dbstr, "candidate") != 0){ clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); goto done; } xkfmt = vec[1]; /* xkfmt = /interface/%s/address/%s --> ^/interface/eth0/address/.*$ --> /interface/[name=eth0]/address */ if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0) goto done; if (xmldb_get(h, dbstr, xkpath, 1, &xt, &xvec, &xlen) < 0) goto done; /* One round to detect duplicates * XXX The code below would benefit from some cleanup */ j = 0; for (i = 0; i < xlen; i++) { char *str; x = xvec[i]; if (xml_type(x) == CX_BODY) bodystr = xml_value(x); else bodystr = xml_body(x); if (bodystr == NULL){ clicon_err(OE_CFG, 0, "No xml body"); goto done; } /* detect duplicates */ for (k=0; kd_name, ".") != 0 && strcmp(dp->d_name, "..") != 0 #else dp->d_name[0] != '.' #endif ) { snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp->d_name); if (lstat(filename, &st) == 0){ if ((st.st_mode & flags) == 0) continue; #if EXPAND_RECURSIVE if (S_ISDIR(st.st_mode)) { int nrsav = *nr; if(expand_dir(filename, nr, commands, detail) < 0) goto quit; while(nrsav < *nr) { len = strlen(dp->d_name) + strlen((*commands)[nrsav]) + 2; if((str = malloc(len)) == NULL) { fprintf(stderr, "expand_dir: malloc: %s\n", strerror(errno)); goto quit; } snprintf(str, len-1, "%s/%s", dp->d_name, (*commands)[nrsav]); free((*commands)[nrsav]); (*commands)[nrsav] = str; nrsav++; } continue; } #endif if ((cmd = strdup(dp->d_name)) == NULL) { fprintf(stderr, "expand_dir: strdup: %s\n", strerror(errno)); goto quit; } if (0 &&detail){ if ((pw = getpwuid(st.st_uid)) == NULL){ fprintf(stderr, "expand_dir: getpwuid(%d): %s\n", st.st_uid, strerror(errno)); goto quit; } len = strlen(cmd) + strlen(pw->pw_name) + #ifdef __FreeBSD__ strlen(ctime(&st.st_mtimespec.tv_sec)) + #else strlen(ctime(&st.st_mtim.tv_sec)) + #endif strlen("{ by }") + 1 /* \0 */; if ((str=realloc(cmd, strlen(cmd)+len)) == NULL) { fprintf(stderr, "expand_dir: malloc: %s\n", strerror(errno)); goto quit; } snprintf(str + strlen(dp->d_name), len - strlen(dp->d_name), "{%s by %s}", #ifdef __FreeBSD__ ctime(&st.st_mtimespec.tv_sec), #else ctime(&st.st_mtim.tv_sec), #endif pw->pw_name ); cmd = str; } if (((*commands) = realloc(*commands, ((*nr)+1)*sizeof(char**))) == NULL){ perror("expand_dir: realloc"); goto quit; } (*commands)[(*nr)] = cmd; (*nr)++; if (*nr >= 128) /* Limit number of options */ break; } } } retval = 0; quit: closedir(dirp); return retval; } /*! Generic function for showing configurations. * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] arg A string: [] * @param[out] xt Configuration as xml tree. * Format of arg: * "running", "candidate" * xpath expression * optional name of variable in cvv. If set, xpath must have a '%s' * @code * show config id , show_conf_as("running interfaces/interface[name=%s] n"); * @endcode */ static int show_conf_as(clicon_handle h, cvec *cvv, cg_var *arg, cxobj **xt) /* top xml */ { int retval = -1; char *db; char **vec = NULL; int nvec; char *str; char *xpath; char *attr = NULL; cbuf *cbx = NULL; int i; int j; cg_var *cvattr; char *val = NULL; if (arg == NULL || (str = cv_string_get(arg)) == NULL){ clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); goto done; } if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); goto done; } if (nvec != 2 && nvec != 3){ clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected []", str); goto done; } /* Dont get attr here, take it from arg instead */ db = vec[0]; if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0) { clicon_err(OE_PLUGIN, 0, "No such db name: %s", db); goto done; } xpath = vec[1]; if ((cbx = cbuf_new()) == NULL){ clicon_err(OE_PLUGIN, errno, "cbuf_new"); goto done; } if (nvec == 3){ attr = vec[2]; j = 0; for (i=0; i [] * @param[in] netconf If set print as netconf edit-config, otherwise just xml * @see show_conf_as the main function */ static int show_conf_as_xml1(clicon_handle h, cvec *cvv, cg_var *arg, int netconf) { cxobj *xt = NULL; cxobj *xc; int retval = -1; if (show_conf_as(h, cvv, arg, &xt) < 0) goto done; if (netconf) /* netconf prefix */ fprintf(stdout, "\n"); xc = NULL; /* Dont print xt itself */ while ((xc = xml_child_each(xt, xc, -1)) != NULL) clicon_xml2file(stdout, xc, netconf?2:0, 1); if (netconf) /* netconf postfix */ fprintf(stdout, "]]>]]>\n"); retval = 0; done: if (xt) xml_free(xt); return retval; } /*! Show configuration as prettyprinted xml * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] arg A string: [] * @see show_conf_as the main function */ int show_conf_as_xml(clicon_handle h, cvec *cvv, cg_var *arg) { return show_conf_as_xml1(h, cvv, arg, 0); } /*! Show configuration as prettyprinted xml with netconf hdr/tail * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] arg A string: [] * @see show_conf_as the main function */ int show_conf_as_netconf(clicon_handle h, cvec *cvv, cg_var *arg) { return show_conf_as_xml1(h, cvv, arg, 1); } /*! Show configuration as JSON * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] arg A string: [] * @see show_conf_as the main function */ int show_conf_as_json(clicon_handle h, cvec *cvv, cg_var *arg) { cxobj *xt = NULL; int retval = -1; if (show_conf_as(h, cvv, arg, &xt) < 0) goto done; xml2json(stdout, xt, 1); retval = 0; done: if (xt) xml_free(xt); return retval; } /*! Show configuration as text givebn an xpath * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] arg A string: * @note Hardcoded that a variable in cvv is named "xpath" */ int show_conf_xpath(clicon_handle h, cvec *cvv, cg_var *arg) { int retval = -1; char *str; char *xpath; cg_var *cv; cxobj *xt = NULL; cxobj **xv = NULL; size_t xlen; int i; if (arg == NULL || (str = cv_string_get(arg)) == NULL){ clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); goto done; } /* Dont get attr here, take it from arg instead */ if (strcmp(str, "running") != 0 && strcmp(str, "candidate") != 0){ clicon_err(OE_PLUGIN, 0, "No such db name: %s", str); goto done; } cv = cvec_find_var(cvv, "xpath"); xpath = cv_string_get(cv); if (xmldb_get(h, str, xpath, 1, &xt, &xv, &xlen) < 0) goto done; for (i=0; i CSV commands * Can only be made in a 'flat tree', ie on the form: * B --> * Type, A * X, B * @param[in] f Output file * @param[in] x XML tree * @param[in] cvv A vector of field names present in XML * This means that only fields in x that are listed in cvv will be printed. */ static int xml2csv(FILE *f, cxobj *x, cvec *cvv) { cxobj *xe, *xb; int retval = -1; cg_var *vs; fprintf(f, "%s", xml_name(x)); xe = NULL; vs = NULL; while ((vs = cvec_each(cvv, vs))) { if ((xe = xml_find(x, cv_name_get(vs))) == NULL){ fprintf(f, ";"); continue; } if (xml_child_nr(xe)){ xb = xml_child_i(xe, 0); fprintf(f, ";%s", xml_value(xb)); } } fprintf(f, "\n"); retval = 0; return retval; } static int show_conf_as_csv1(clicon_handle h, cvec *cvv0, cg_var *arg) { cxobj *xt = NULL; cxobj *xc; int retval = -1; cvec *cvv=NULL; char *str; if (show_conf_as(h, cvv0, arg, &xt) < 0) goto done; xc = NULL; /* Dont print xt itself */ while ((xc = xml_child_each(xt, xc, -1)) != NULL){ if ((str = chunk_sprintf(__FUNCTION__, "%s[]", xml_name(xc))) == NULL) goto done; #ifdef NOTYET /* yang-spec? */ if (ds==NULL && (ds = key2spec_key(dbspec, str)) != NULL){ cg_var *vs; fprintf(stdout, "Type"); cvv = db_spec2cvec(ds); vs = NULL; while ((vs = cvec_each(cvv, vs))) fprintf(stdout, ";%s", cv_name_get(vs)); fprintf(stdout, "\n"); } /* Now values just need to follow,... */ #endif /* yang-spec? */ if (cvv== NULL) goto done; xml2csv(stdout, xc, cvv); /* csv syntax */ } retval = 0; done: if (xt) xml_free(xt); unchunk_group(__FUNCTION__); return retval; } int show_conf_as_csv(clicon_handle h, cvec *cvv, cg_var *arg) { return show_conf_as_csv1(h, cvv, arg); }