/* alloc help-function: double xv vectors. spcial case for init */ static int xv_alloc(struct xml_node ***xv0p, struct xml_node ***xv1p, int *xv_maxp) { struct xml_node **xv0 = *xv0p; struct xml_node **xv1 = *xv1p; int xv_max0 = *xv_maxp; int xv_max; int init; init = (xv0p == NULL); if (init) xv_max = xv_max0; else xv_max = 2*xv_max0; if ((xv0 = realloc(xv0, sizeof(struct xml_node *) * xv_max)) == NULL){ clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__); return -1; } if (init) memset(xv0, 0, xv_max); else memset(xv0+xv_max0, 0, xv_max-xv_max0); if ((xv1 = realloc(xv1, sizeof(struct xml_node *) * xv_max)) == NULL){ clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__); return -1; } if (init) memset(xv1, 0, xv_max); else memset(xv1+xv_max0, 0, xv_max-xv_max0); *xv0p = xv0; *xv1p = xv1; *xv_maxp = xv_max; return 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; }
char * clicon_sha1hex(const char *str) { int i; char *retstr; SHA1Context context; SHA1Reset(&context); SHA1Input(&context, (const unsigned char *)str, strlen(str)); if (!SHA1Result(&context)) { clicon_err(OE_UNIX, 0, "Could not compute SHA1 message digest"); return NULL; } /* 160 bits = 20 bytes = 40 hexdigits + '\0' */ if ((retstr = malloc(41)) == NULL) { clicon_err(OE_UNIX, errno, "malloc"); return NULL; } for(i = 0; i < 5 ; i++) sprintf(retstr + (i*8), "%.8x", context.Message_Digest[i]); return retstr; }
/* * Read procut number and serialnr from product file, and set environment vars: * typically RNR_PRODUCT and RNR_SERIALNR * Then call the rost_get_ functions above */ int rost_product2env() { FILE *f; char line[1024]; char *name, *val; if (is_initialized) return 0; if ((f = fopen (ROST_PRODUCT_FILE, "r")) == NULL){ #if 0 /* ignored */ clicon_err(OE_UNIX, errno, "Failed to open %s", ROST_PRODUCT_FILE); return -1; #else return 0; #endif } while (fgets(line, sizeof(line), f) != NULL){ line[strlen(line)-1] = '\0'; val = line; name = strsep(&val, "="); if (setenv(name, val, 1) < 0){ clicon_err(OE_UNIX, errno, "setenv"); return -1; } } fclose(f); is_initialized = 1; return 0; }
/*! Compare two dbs using XML. Write to file and run diff */ static int compare_xmls(cxobj *xc1, cxobj *xc2, int astext) { int fd; FILE *f; char filename1[MAXPATHLEN]; char filename2[MAXPATHLEN]; char cmd[MAXPATHLEN]; int retval = -1; cxobj *xc; snprintf(filename1, sizeof(filename1), "/tmp/cliconXXXXXX"); snprintf(filename2, sizeof(filename2), "/tmp/cliconXXXXXX"); if ((fd = mkstemp(filename1)) < 0){ clicon_err(OE_UNDEF, errno, "tmpfile: %s", strerror (errno)); goto done; } if ((f = fdopen(fd, "w")) == NULL) goto done; xc = NULL; if (astext) while ((xc = xml_child_each(xc1, xc, -1)) != NULL) xml2txt(f, xc, 0); else while ((xc = xml_child_each(xc1, xc, -1)) != NULL) xml_print(f, xc); fclose(f); close(fd); if ((fd = mkstemp(filename2)) < 0){ clicon_err(OE_UNDEF, errno, "mkstemp: %s", strerror (errno)); goto done; } if ((f = fdopen(fd, "w")) == NULL) goto done; xc = NULL; if (astext) while ((xc = xml_child_each(xc2, xc, -1)) != NULL) xml2txt(f, xc, 0); else while ((xc = xml_child_each(xc2, xc, -1)) != NULL) xml_print(f, xc); fclose(f); close(fd); snprintf(cmd, sizeof(cmd), "/usr/bin/diff -dU 1 %s %s | grep -v @@ | sed 1,2d", filename1, filename2); if (system(cmd) < 0) goto done; retval = 0; done: unlink(filename1); unlink(filename2); return retval; }
/*! Register log notification stream * @param[in] h Clicon handle * @param[in] stream Event stream. CLICON is predefined, others are application-defined * @param[in] filter Filter. For xml notification ie xpath: .[name="kalle"] * @param[in] status 0 for stop, 1 to start * @param[in] fn Callback function called when notification occurs * @param[in] arg Argument to function note * Note this calls cligen_regfd which may callback on cli command interpretator */ int cli_notification_register(clicon_handle h, char *stream, enum format_enum format, char *filter, int status, int (*fn)(int, void*), void *arg) { int retval = -1; char *logname = NULL; void *p; int s; clicon_hash_t *cdat = clicon_data(h); size_t len; int s_exist = -1; len = strlen("log_socket_") + strlen(stream) + 1; if ((logname = malloc(len)) == NULL){ clicon_err(OE_UNIX, errno, "malloc"); goto done; } snprintf(logname, len, "log_socket_%s", stream); if ((p = hash_value(cdat, logname, &len)) != NULL) s_exist = *(int*)p; if (status){ /* start */ if (s_exist!=-1){ clicon_err(OE_PLUGIN, 0, "Result log socket already exists"); goto done; } if (clicon_rpc_create_subscription(h, stream, filter, &s) < 0) goto done; if (cligen_regfd(s, fn, arg) < 0) goto done; if (hash_add(cdat, logname, &s, sizeof(s)) == NULL) goto done; } else{ /* stop */ if (s_exist != -1){ cligen_unregfd(s_exist); } hash_del(cdat, logname); #if 0 /* cant turn off */ if (clicon_rpc_create_subscription(h, status, stream, format, filter, NULL) < 0) goto done; #endif } retval = 0; done: if (logname) free(logname); return retval; }
/*! 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; }
/*! Send a debug request to backend server * @param[in] h CLICON handle * @param[in] level Debug level * @retval 0 OK * @retval -1 Error and logged to syslog */ int clicon_rpc_debug(clicon_handle h, int level) { int retval = -1; struct clicon_msg *msg = NULL; cxobj *xret = NULL; cxobj *xerr; char *username; username = clicon_username_get(h); /* XXX: hardcoded example yang, should be clixon-config!!! */ if ((msg = clicon_msg_encode("<rpc username=\"%s\"><debug xmlns=\"http://clicon.org/lib\"><level>%d</level></debug></rpc>", username?username:"", level)) == NULL) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Debug",xerr); goto done; } if (xpath_first(xret, "//rpc-reply/ok") == NULL){ clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */ goto done; } retval = 0; done: if (msg) free(msg); if (xret) xml_free(xret); return retval; }
/* * plugin call */ static int plugin_call(clicon_handle h, char *func) { int retval = -1; PyObject *handle = NULL; PyObject *cli = NULL; PyObject *val = NULL; if (Py_IsInitialized() == 0) return 0; PyErr_Clear(); if ((cli = PyImport_ImportModule("cliconcli")) == NULL) goto quit; if ((handle = PyCapsule_New((void *)h, NULL, NULL)) == NULL) goto quit; val = PyObject_CallMethod(cli, func, "O", handle); if (val == NULL) goto quit; retval = PyLong_AsLong(val); quit: if (PyErr_Occurred()) clicon_err(OE_PLUGIN, 0, "%s", ErrStringVerbose(0)); Py_XDECREF(cli); Py_XDECREF(handle); Py_XDECREF(val); return retval; }
/*! Merge xml in filename into database * @retval -1 Error * @retval 0 Validation failed (with cbret set) * @retval 1 Validation OK */ static int load_extraxml(clicon_handle h, char *filename, const char *db, cbuf *cbret) { int retval = -1; cxobj *xt = NULL; int fd = -1; if (filename == NULL) return 1; if ((fd = open(filename, O_RDONLY)) < 0){ clicon_err(OE_UNIX, errno, "open(%s)", filename); goto done; } if (xml_parse_file(fd, "</config>", NULL, &xt) < 0) goto done; /* Replace parent w first child */ if (xml_rootchild(xt, 0, &xt) < 0) goto done; /* Merge user reset state */ retval = xmldb_put(h, (char*)db, OP_MERGE, xt, clicon_username_get(h), cbret); done: if (fd != -1) close(fd); if (xt) xml_free(xt); return retval; }
/*! Unload all plugins: call exit function and close shared handle * @param[in] h Clicon handle */ int clixon_plugin_exit(clicon_handle h) { clixon_plugin *cp; plgexit_t *exitfn; int i; char *error; for (i = 0; i < _clixon_nplugins; i++) { cp = &_clixon_plugins[i]; if ((exitfn = cp->cp_api.ca_exit) == NULL) continue; if (exitfn(h) < 0) { clicon_debug(1, "plugin_exit() failed"); return -1; } if (dlclose(cp->cp_handle) != 0) { error = (char*)dlerror(); clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error"); } } if (_clixon_plugins){ free(_clixon_plugins); _clixon_plugins = NULL; } _clixon_nplugins = 0; return 0; }
/*! Generate and log clicon error function call from Netconf error message * @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error> */ int clicon_rpc_generate_error(char *format, cxobj *xerr) { int retval = -1; cbuf *cb = NULL; cxobj *x; if ((cb = cbuf_new()) ==NULL){ clicon_err(OE_XML, errno, "cbuf_new"); goto done; } if ((x=xpath_first(xerr, "error-type"))!=NULL) cprintf(cb, "%s ", xml_body(x)); if ((x=xpath_first(xerr, "error-tag"))!=NULL) cprintf(cb, "%s ", xml_body(x)); if ((x=xpath_first(xerr, "error-message"))!=NULL) cprintf(cb, "%s ", xml_body(x)); if ((x=xpath_first(xerr, "error-info"))!=NULL) clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0); clicon_log(LOG_ERR, "%s: %s", format, cbuf_get(cb)); retval = 0; done: if (cb) cbuf_free(cb); return retval; }
/*! Clixon startup startup mode: Commit startup configuration into running state * @param[in] h Clixon handle * @param[in] db tmp or startup * @param[out] cbret If status is invalid contains error message * @retval -1 Error * @retval 0 Validation failed * @retval 1 OK OK: reset running |--------+------------> RUNNING parse validate OK / commit startup -------+--+-------+------------+ INVALID (requires manual edit of candidate) failsafe ----------------------+ reset \ commit running |-------+---------------> RUNNING FAILSAFE parse validate fail startup ---+-------------------------------------> INVALID XML ERR: (requires repair of startup) NYI failsafe ----------------------+ reset \ commit running |-------+---------------> RUNNING FAILSAFE parse fail startup --+-------------------------------------> BROKEN XML * @note: if commit fails, copy factory to running */ int startup_mode_startup(clicon_handle h, char *db, cbuf *cbret) { int retval = -1; int ret; if (strcmp(db, "running")==0){ clicon_err(OE_FATAL, 0, "Invalid startup db: %s", db); goto done; } /* Load plugins and call plugin_init() */ if (backend_plugin_initiate(h) != 0) goto done; /* If startup does not exist, create it empty */ if (xmldb_exists(h, db) != 1){ /* diff */ if (xmldb_create(h, db) < 0) /* diff */ return -1; } if ((ret = startup_commit(h, db, cbret)) < 0) goto done; if (ret == 0) goto fail; retval = 1; done: return retval; fail: retval = 0; goto done; }
/*! * \brief Special error message for quagga routing, using clicon_err as backend * * This is a kludge to replace clicon_err(OE_ROUTING..) calls with * application-specific code inside clicon. We have now moved it outside * of clicon and placed it here instead. */ int rost_err(int suberr, char *reason, ...) { va_list args; struct errvec *ev; char *errstr; int len; char *msg = NULL; int retval = -1; for (ev=QV; ev->ev_err != -1; ev++) if (ev->ev_err == suberr) break; errstr = ev?(ev->ev_str?ev->ev_str:"unknown"):"ROST unknown error"; /* first round: compute length of error message */ va_start(args, reason); len = vsnprintf(NULL, 0, reason, args); va_end(args); /* Allocate message buffer */ if ((msg = malloc(len+1)) == NULL) { clicon_err(OE_UNIX, errno, "malloc"); goto done; } /* second round: compute write message from reason and args */ va_start(args, reason); if (vsnprintf(msg, len+1, reason, args) < 0){ va_end(args); fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */ goto done; } va_end(args); retval = clicon_err(OE_ROUTING, 0, "%s: %s", msg, errstr); done: if (msg) free(msg); return retval; }
/* * XXX: avoid /proc */ int rost_uptime(int *sec) { FILE *f; double u; /* seconds */ /* Open /proc filesystem */ if ((f = fopen ("/proc/uptime", "r")) == NULL){ clicon_err(OE_UNIX, errno, "Failed to open /proc/uptime"); return -1; } if (fscanf(f, "%lf", &u) != 1){ clicon_err(OE_UNIX, errno, "Error when reading /proc/uptime"); fclose(f); return -1; } fclose(f); *sec = (int)u; return 0; }
/* * netconf_plugin_load * Load allplugins you can find in CLICON_NETCONF_DIR */ int netconf_plugin_load(clicon_handle h) { int retval = -1; char *dir; int ndp; struct dirent *dp; int i; char *filename; plghndl_t *handle; if ((dir = clicon_netconf_dir(h)) == NULL) goto quit; /* Get plugin objects names from plugin directory */ if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0) goto quit; /* Load all plugins */ for (i = 0; i < ndp; i++) { filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name); clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...", (int)strlen(filename), filename); if (filename == NULL) { clicon_err(OE_UNIX, errno, "chunk"); goto quit; } if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL) goto quit; if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) { clicon_err(OE_UNIX, errno, "chunk"); goto quit; } plugins[nplugins++] = handle; unchunk (filename); } retval = 0; quit: unchunk_group(__FUNCTION__); return retval; }
/* * netconf_register_callback * Called from plugin to register a callback for a specific netconf XML tag. */ int netconf_register_callback(clicon_handle h, netconf_cb_t cb, /* Callback called */ void *arg, /* Arg to send to callback */ char *tag) /* Xml tag when callback is made */ { netconf_reg_t *nr; if ((nr = malloc(sizeof(netconf_reg_t))) == NULL) { clicon_err(OE_DB, errno, "malloc: %s", strerror(errno)); goto catch; }
int vlan_add_interface(void *handle, cvec *vars, cg_var *arg) { char *dot; cg_var *cv; cg_var *ifname; char *str; if ((ifname = cvec_find_var(vars, "name")) == NULL) { clicon_err(OE_CFG, ENOENT, "Could not find 'name' in variable vector"); goto catch; }
/*! This is the callback used by cli_setlog to print log message in CLI * param[in] s UNIX socket from backend where message should be read * param[in] arg format: txt, xml, xml2txt, xml2json */ static int cli_notification_cb(int s, void *arg) { struct clicon_msg *reply = NULL; int eof; int retval = -1; cxobj *xt = NULL; cxobj *xe; cxobj *x; enum format_enum format = (enum format_enum)arg; /* get msg (this is the reason this function is called) */ if (clicon_msg_rcv(s, &reply, &eof) < 0) goto done; if (eof){ clicon_err(OE_PROTO, ESHUTDOWN, "Socket unexpected close"); close(s); errno = ESHUTDOWN; event_unreg_fd(s, cli_notification_cb); goto done; } if (clicon_msg_decode(reply, NULL, &xt) < 0) /* XXX pass yang_spec */ goto done; if ((xe = xpath_first(xt, "//event")) != NULL){ x = NULL; while ((x = xml_child_each(xe, x, -1)) != NULL) { switch (format){ case FORMAT_XML: if (clicon_xml2file(stdout, x, 0, 1) < 0) goto done; break; case FORMAT_TEXT: if (xml2txt(stdout, x, 0) < 0) goto done; break; case FORMAT_JSON: if (xml2json(stdout, x, 1) < 0) goto done; break; default: break; } } } retval = 0; done: if (xt) xml_free(xt); if (reply) free(reply); return retval; }
/*! Reset running and start in failsafe mode. If no failsafe then quit. Typically done when startup status is not OK so failsafe ----------------------+ reset \ commit running |-------+---------------> RUNNING FAILSAFE */ int startup_failsafe(clicon_handle h) { int retval = -1; int ret; char *db = "failsafe"; cbuf *cbret = NULL; if ((cbret = cbuf_new()) == NULL){ clicon_err(OE_XML, errno, "cbuf_new"); goto done; } if ((ret = xmldb_exists(h, db)) < 0) goto done; if (ret == 0){ /* No it does not exist, fail */ clicon_err(OE_DB, 0, "Startup failed and no Failsafe database found, exiting"); goto done; } /* Copy original running to tmp as backup (restore if error) */ if (xmldb_copy(h, "running", "tmp") < 0) goto done; if (startup_db_reset(h, "running") < 0) goto done; ret = candidate_commit(h, db, cbret); if (ret != 1) if (xmldb_copy(h, "tmp", "running") < 0) goto done; if (ret < 0) goto done; if (ret == 0){ clicon_err(OE_DB, 0, "Startup failed, Failsafe database validation failed %s", cbuf_get(cbret)); goto done; } clicon_log(LOG_NOTICE, "Startup failed, Failsafe database loaded "); retval = 0; done: if (cbret) cbuf_free(cbret); return retval; }
/* * Load a dynamic plugin object and call it's init-function * Note 'file' may be destructively modified */ static plghndl_t plugin_load (clicon_handle h, char *file, int dlflags, const char *cnklbl) { char *error; void *handle = NULL; plginit_t *initfn; dlerror(); /* Clear any existing error */ if ((handle = dlopen (file, dlflags)) == NULL) { error = (char*)dlerror(); clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error"); goto quit; } /* call plugin_init() if defined */ if ((initfn = dlsym(handle, "plugin_init")) != NULL) { if (initfn(h) != 0) { clicon_err(OE_PLUGIN, errno, "Failed to initiate %s\n", strrchr(file,'/')?strchr(file, '/'):file); goto quit; } } quit: return handle; }
/* * from_client_commit * Handle an incoming commit message from a client. * XXX: If commit succeeds and snapshot/startup fails, we have strange state: * the commit has succeeded but an error message is returned. */ int from_client_commit(clicon_handle h, int s, struct clicon_msg *msg, const char *label) { char *candidate, *running; uint32_t snapshot, startup; int retval = -1; char *snapshot_0; if (clicon_msg_commit_decode(msg, &candidate, &running, &snapshot, &startup, label) < 0) goto err; if (candidate_commit(h, candidate, running) < 0){ clicon_debug(1, "Commit %s failed", candidate); retval = 0; /* We ignore errors from commit, but maybe we should fail on fatal errors? */ goto err; } clicon_debug(1, "Commit %s", candidate); if (snapshot && config_snapshot(clicon_dbspec_key(h), running, clicon_archive_dir(h)) < 0) goto err; if (startup){ snapshot_0 = chunk_sprintf(__FUNCTION__, "%s/0", clicon_archive_dir(h)); if (file_cp(snapshot_0, clicon_startup_config(h)) < 0){ clicon_err(OE_PROTO, errno, "%s: Error when creating startup", __FUNCTION__); goto err; } } retval = 0; if (send_msg_ok(s) < 0) goto done; goto done; err: /* XXX: more elaborate errstring? */ if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0) retval = -1; done: unchunk_group(__FUNCTION__); return retval; /* may be zero if we ignoring errors from commit */ } /* from_client_commit */
/*! 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; }
/*! Add new client, typically frontend such as cli, netconf, restconf * @param[in] h Clicon handle * @param[in] addr Address of client * @retval ce Client entry * @retval NULL Error */ struct client_entry * backend_client_add(clicon_handle h, struct sockaddr *addr) { struct backend_handle *bh = handle(h); struct client_entry *ce; if ((ce = (struct client_entry *)malloc(sizeof(*ce))) == NULL){ clicon_err(OE_PLUGIN, errno, "malloc"); return NULL; } memset(ce, 0, sizeof(*ce)); ce->ce_nr = bh->bh_ce_nr++; memcpy(&ce->ce_addr, addr, sizeof(*addr)); ce->ce_next = bh->bh_ce_list; bh->bh_ce_list = ce; return ce; }
/* * If commit is successful the current db has been replaced by the committed * candidate. We simply re-create a new NTP_CONF based on it. */ int transaction_end(clicon_handle h) { FILE *out = NULL; if (dns_reload == 0) return 0; /* Nothing has changed */ if ((out = fopen(RESOLV_CONF, "w")) == NULL) { clicon_err(OE_CFG, errno, "%s: fopen", __FUNCTION__); return -1; } lvmap_print(out, clicon_running_db(h), resolv_conf_fmts, NULL); fclose(out); return 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; }
/*! 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; }
/* * Unload a plugin */ static int plugin_unload(clicon_handle h, void *handle) { int retval = 0; char *error; plgexit_t *exitfn; /* Call exit function is it exists */ exitfn = dlsym(handle, "plugin_exit"); if (dlerror() == NULL) exitfn(h); dlerror(); /* Clear any existing error */ if (dlclose(handle) != 0) { error = (char*)dlerror(); clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error"); /* Just report */ } return retval; }
/*! Load a set of plugin objects from a directory and and call their init-function * @param[in] h Clicon handle * @param[in] function Which function symbol to load and call (eg CLIXON_PLUGIN_INIT) * @param[in] dir Directory. .so files in this dir will be loaded. * @param[in] regexp Regexp for matching files in plugin directory. Default *.so. * @retval 0 OK * @retval -1 Error */ int clixon_plugins_load(clicon_handle h, char *function, char *dir, char *regexp) { int retval = -1; int ndp; struct dirent *dp = NULL; int i; char filename[MAXPATHLEN]; clixon_plugin *cp; int ret; clicon_debug(1, "%s", __FUNCTION__); /* Get plugin objects names from plugin directory */ if((ndp = clicon_file_dirent(dir, &dp, regexp?regexp:"(.so)$", S_IFREG)) < 0) goto done; /* Load all plugins */ for (i = 0; i < ndp; i++) { snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name); clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...", (int)strlen(filename), filename); if ((ret = plugin_load_one(h, filename, function, RTLD_NOW, &cp)) < 0) goto done; if (ret == 0) continue; _clixon_nplugins++; if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) { clicon_err(OE_UNIX, errno, "realloc"); goto done; } _clixon_plugins[_clixon_nplugins-1] = *cp; free(cp); } retval = 0; done: if (dp) free(dp); return retval; }
/*! 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; }