/* * ***** 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 #include #include #include #include /* cligen */ #include /* clicon */ #include #include "clixon_xmldb_text.h" /*! Database locking for candidate and running non-persistent * Store an integer for running and candidate containing * the session-id of the client holding the lock. */ static int _running_locked = 0; static int _candidate_locked = 0; static int _startup_locked = 0; /*! Translate from symbolic database name to actual filename in file-system * @param[in] h Clicon handle * @param[in] db Symbolic database name, eg "candidate", "running" * @param[out] filename Filename. Unallocate after use with free() * @retval 0 OK * @retval -1 Error * @note Could need a way to extend which databases exists, eg to register new. * The currently allowed databases are: * candidate, tmp, running, result * The filename reside in CLICON_XMLDB_DIR option */ static int db2file(clicon_handle h, char *db, char **filename) { int retval = -1; cbuf *cb; char *dir; if ((cb = cbuf_new()) == NULL){ clicon_err(OE_XML, errno, "cbuf_new"); goto done; } if ((dir = clicon_xmldb_dir(h)) == NULL){ clicon_err(OE_XML, errno, "CLICON_XMLDB_DIR not set"); goto done; } if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0 && strcmp(db, "startup") != 0 && strcmp(db, "tmp") != 0){ clicon_err(OE_XML, 0, "Unexpected database: %s", db); goto done; } cprintf(cb, "%s/%s_db", dir, db); if ((*filename = strdup4(cbuf_get(cb))) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); goto done; } retval = 0; done: if (cb) cbuf_free(cb); return retval; } /*! Get content of database using xpath. return a set of matching sub-trees * The function returns a minimal tree that includes all sub-trees that match * xpath. * @param[in] dbname Name of database to search in (filename including dir path * @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] yspec Yang specification * @param[out] xtop Single XML tree which xvec points to. Free with xml_free() * @param[out] xvec Vector of xml trees. Free after use. * @param[out] xlen Length of vector. * @retval 0 OK * @retval -1 Error * @code * cxobj *xt; * cxobj **xvec; * size_t xlen; * yang_spec *yspec = clicon_dbspec_yang(h); * if (xmldb_get("running", "/interfaces/interface[name="eth"]", * &xt, &xvec, &xlen) < 0) * err; * for (i=0; i17", &xt) < 0) * err; * if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0) * err; * @endcode * @see xmldb_put_xkey for single key */ int text_put(clicon_handle h, char *db, enum operation_type op, char *api_path, cxobj *xt) { int retval = -1; retval = 0; // done: return retval; } /*! Raw dump of database, just keys and values, no xml interpretation * @param[in] f File * @param[in] dbfile File-name of database. This is a local file * @param[in] rxkey Key regexp, eg "^.*$" * @note This function can only be called locally. */ int text_dump(FILE *f, char *dbfilename, char *rxkey) { int retval = -1; retval = 0; // done: return retval; } /*! Copy database from db1 to db2 * @param[in] h Clicon handle * @param[in] from Source database copy * @param[in] to Destination database * @retval -1 Error * @retval 0 OK */ int text_copy(clicon_handle h, char *from, char *to) { int retval = -1; retval = 0; // done: return retval; } /*! Lock database * @param[in] h Clicon handle * @param[in] db Database * @param[in] pid Process id * @retval -1 Error * @retval 0 OK */ int text_lock(clicon_handle h, char *db, int pid) { if (strcmp("running", db) == 0) _running_locked = pid; else if (strcmp("candidate", db) == 0) _candidate_locked = pid; else if (strcmp("startup", db) == 0) _startup_locked = pid; clicon_debug(1, "%s: locked by %u", db, pid); return 0; } /*! Unlock database * @param[in] h Clicon handle * @param[in] db Database * @param[in] pid Process id * @retval -1 Error * @retval 0 OK * Assume all sanity checks have been made */ int text_unlock(clicon_handle h, char *db, int pid) { if (strcmp("running", db) == 0) _running_locked = 0; else if (strcmp("candidate", db) == 0) _candidate_locked = 0; else if (strcmp("startup", db) == 0) _startup_locked = 0; return 0; } /*! Unlock all databases locked by pid (eg process dies) * @param[in] h Clicon handle * @param[in] pid Process / Session id * @retval -1 Error * @retval 0 Ok */ int text_unlock_all(clicon_handle h, int pid) { if (_running_locked == pid) _running_locked = 0; if (_candidate_locked == pid) _candidate_locked = 0; if (_startup_locked == pid) _startup_locked = 0; return 0; } /*! Check if database is locked * @param[in] h Clicon handle * @param[in] db Database * @retval -1 Error * @retval 0 Not locked * @retval >0 Id of locker */ int text_islocked(clicon_handle h, char *db) { if (strcmp("running", db) == 0) return (_running_locked); else if (strcmp("candidate", db) == 0) return(_candidate_locked); else if (strcmp("startup", db) == 0) return(_startup_locked); return 0; } /*! Check if db exists * @param[in] h Clicon handle * @param[in] db Database * @retval -1 Error * @retval 0 No it does not exist * @retval 1 Yes it exists */ int text_exists(clicon_handle h, char *db) { int retval = -1; char *filename = NULL; struct stat sb; if (db2file(h, db, &filename) < 0) goto done; if (lstat(filename, &sb) < 0) retval = 0; else retval = 1; done: if (filename) free(filename); return retval; } /*! Delete database. Remove file * @param[in] h Clicon handle * @param[in] db Database * @retval -1 Error * @retval 0 OK */ int text_delete(clicon_handle h, char *db) { int retval = -1; retval = 0; // done: return retval; } /*! Initialize database * @param[in] h Clicon handle * @param[in] db Database * @retval 0 OK * @retval -1 Error */ int text_init(clicon_handle h, char *db) { int retval = -1; retval = 0; // done: return retval; } /*! plugin init function */ int text_plugin_exit(void) { return 0; } static const struct xmldb_api api; /*! plugin init function */ void * clixon_xmldb_plugin_init(int version) { if (version != XMLDB_API_VERSION){ clicon_err(OE_DB, 0, "Invalid version %d expected %d", version, XMLDB_API_VERSION); goto done; } return (void*)&api; done: return NULL; } static const struct xmldb_api api = { 1, XMLDB_API_MAGIC, clixon_xmldb_plugin_init, text_plugin_exit, text_get, text_put, text_dump, text_copy, text_lock, text_unlock, text_unlock_all, text_islocked, text_exists, text_delete, text_init, }; #if 0 /* Test program */ /* * Turn this on to get an xpath test program * Usage: clicon_xpath [] * read xml from input * Example compile: gcc -g -o xmldb -I. -I../clixon ./clixon_xmldb.c -lclixon -lcligen */ static int usage(char *argv0) { fprintf(stderr, "usage:\n%s\tget []\t\txml on stdin\n", argv0); fprintf(stderr, "\tput set|merge|delete\txml to stdout\n"); exit(0); } int main(int argc, char **argv) { cxobj *xt; cxobj *xn; char *xpath; enum operation_type op; char *cmd; char *db; char *yangdir; char *yangmod; yang_spec *yspec = NULL; clicon_handle h; if ((h = clicon_handle_init()) == NULL) goto done; clicon_log_init("xmldb", LOG_DEBUG, CLICON_LOG_STDERR); if (argc < 4){ usage(argv[0]); goto done; } cmd = argv[1]; db = argv[2]; yangdir = argv[3]; yangmod = argv[4]; db_init(db); if ((yspec = yspec_new()) == NULL) goto done if (yang_parse(h, yangdir, yangmod, NULL, yspec) < 0) goto done; if (strcmp(cmd, "get")==0){ if (argc < 5) usage(argv[0]); xpath = argc>5?argv[5]:NULL; if (xmldb_get(h, db, xpath, &xt, NULL, NULL) < 0) goto done; clicon_xml2file(stdout, xt, 0, 1); } else if (strcmp(cmd, "put")==0){ if (argc != 6) usage(argv[0]); if (clicon_xml_parse_file(0, &xt, "") < 0) goto done; if (xml_rootchild(xt, 0, &xn) < 0) goto done; if (strcmp(argv[5], "set") == 0) op = OP_REPLACE; else if (strcmp(argv[4], "merge") == 0) op = OP_MERGE; else if (strcmp(argv[5], "delete") == 0) op = OP_REMOVE; else usage(argv[0]); if (xmldb_put(h, db, op, NULL, xn) < 0) goto done; } else usage(argv[0]); printf("\n"); done: return 0; } #endif /* Test program */