/* * Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren This file is part of CLIXON. CLIXON is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. CLIXON is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CLIXON; see the file LICENSE. If not, see . * */ #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 *xk = NULL; cxobj **xvec = NULL; size_t xlen = 0; cxobj *x; char *bodystr; int i; 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 a[].b[] $!x $!y" */ 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, &xk) < 0) goto done; if (xmldb_get(h, dbstr, xk, 1, &xt, &xvec, &xlen) < 0) goto done; i0 = *nr; *nr += xlen; if ((*commands = realloc(*commands, sizeof(char *) * (*nr))) == NULL) { clicon_err(OE_UNIX, errno, "realloc: %s", strerror (errno)); goto done; } for (i = 0; i < xlen; i++) { 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; } (*commands)[i0+i] = strdup(bodystr); } retval = 0; done: unchunk_group(__FUNCTION__); if (xvec) free(xvec); if (xt) xml_free(xt); if (xk) free(xk); return retval; } /*! List files in a directory */ int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail) { DIR *dirp; struct dirent *dp; struct stat st; char *str; char *cmd; int len; int retval = -1; struct passwd *pw; char filename[MAXPATHLEN]; if ((dirp = opendir(dir)) == 0){ fprintf(stderr, "expand_dir: opendir(%s) %s\n", dir, strerror(errno)); return -1; } *nr = 0; while ((dp = readdir(dirp)) != NULL) { if ( #if 0 strcmp(dp->d_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; cxobj *xc; int retval = -1; if (show_conf_as(h, cvv, arg, &xt) < 0) goto done; xc = NULL; /* Dont print xt itself */ while ((xc = xml_child_each(xt, xc, -1)) != NULL) xml2json(stdout, xc, 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); }