Use the new RPC error reporting interface to report RPC errors from plugins. Signed-off-by: Corey Minyard <minyard@acm.org>
974 lines
30 KiB
C
974 lines
30 KiB
C
/*
|
|
*
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
|
Copyright (C) 2017-2019 Olof Hagsand
|
|
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
|
|
|
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 <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <dlfcn.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <syslog.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
#include <netinet/in.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clixon */
|
|
#include <clixon/clixon.h>
|
|
|
|
#include "clixon_backend_transaction.h"
|
|
#include "clixon_backend_plugin.h"
|
|
#include "clixon_backend_commit.h"
|
|
|
|
/*! Request plugins to reset system state
|
|
*
|
|
* The system 'state' should be the same as the contents of running_db
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] db Name of datastore
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_plugin_reset_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
char *db)
|
|
{
|
|
int retval = -1;
|
|
plgreset_t *fn; /* callback */
|
|
void *wh = NULL;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_reset) != NULL){
|
|
wh = NULL;
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (fn(h, db) < 0) {
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (clixon_err_category() < 0)
|
|
clixon_log(h, LOG_WARNING, "%s: Internal error: Reset callback in plugin: %s returned -1 but did not make a clixon_err call",
|
|
__FUNCTION__, clixon_plugin_name_get(cp));
|
|
goto done;
|
|
}
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call all plugins reset callbacks
|
|
*
|
|
* The system 'state' should be the same as the contents of running_db
|
|
* @param[in] h Clixon handle
|
|
* @param[in] db Name of datastore
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_plugin_reset_all(clixon_handle h,
|
|
char *db)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
/* Loop through all plugins, call callbacks in each */
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (clixon_plugin_reset_one(cp, h, db) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single plugin "pre-" daemonize callback
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
clixon_plugin_pre_daemon_one(clixon_plugin_t *cp,
|
|
clixon_handle h)
|
|
{
|
|
int retval = -1;
|
|
plgdaemon_t *fn; /* Daemonize plugin callback function */
|
|
void *wh = NULL;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_pre_daemon) != NULL){
|
|
wh = NULL;
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (fn(h) < 0) {
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (clixon_err_category() < 0)
|
|
clixon_log(h, LOG_WARNING, "%s: Internal error: Pre-daemon callback in plugin:\
|
|
%s returned -1 but did not make a clixon_err call",
|
|
__FUNCTION__, clixon_plugin_name_get(cp));
|
|
goto done;
|
|
}
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call all plugins "pre-" daemonize callbacks
|
|
*
|
|
* This point in time is after "start" and before
|
|
* before daemonization/fork,
|
|
* It is not called if backend is started in daemon mode.
|
|
* @param[in] h Clixon handle
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_plugin_pre_daemon_all(clixon_handle h)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
/* Loop through all plugins, call callbacks in each */
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (clixon_plugin_pre_daemon_one(cp, h) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single plugin "post-" daemonize callback
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
clixon_plugin_daemon_one(clixon_plugin_t *cp,
|
|
clixon_handle h)
|
|
{
|
|
int retval = -1;
|
|
plgdaemon_t *fn; /* Daemonize plugin callback function */
|
|
void *wh = NULL;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_daemon) != NULL){
|
|
wh = NULL;
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (fn(h) < 0) {
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (clixon_err_category() < 0)
|
|
clixon_log(h, LOG_WARNING, "%s: Internal error: Daemon callback in plugin: %s returned -1 but did not make a clixon_err call",
|
|
__FUNCTION__, clixon_plugin_name_get(cp));
|
|
goto done;
|
|
}
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call all plugins "post-" daemonize callbacks
|
|
*
|
|
* This point in time is after "start" and after "pre-daemon" and
|
|
* after daemonization/fork, ie when
|
|
* daemon is in the background but before dropped privileges.
|
|
* In case of foreground mode (-F) it is still called but no fork has occured.
|
|
* @param[in] h Clixon handle
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @note Also called for non-background mode
|
|
*/
|
|
int
|
|
clixon_plugin_daemon_all(clixon_handle h)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
/* Loop through all plugins, call callbacks in each */
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (clixon_plugin_daemon_one(cp, h) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single backend statedata callback
|
|
*
|
|
* Create an xml state tree (xret) for one callback only on the form:
|
|
* <config>...</config>,
|
|
* call a user supplied function (ca_statedata) which can do two things:
|
|
* - Fill in state XML in the tree and return 0
|
|
* - Call cli_error() and return -1
|
|
* In the former case, this function returns the state XML tree to the caller (which
|
|
* typically merges the tree with other state trees).
|
|
* In the latter error case, this function returns 0 (invalid) to the caller with no tree
|
|
* If a fatal error occurs in this function, -1 is returned.
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h clicon handle
|
|
* @param[in] nsc namespace context for xpath
|
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
|
* @param[out] xp If retval=1, state tree created and returned: <config>...
|
|
* @retval 1 OK if callback found (and called) xret is set
|
|
* @retval 0 Statedata callback failed. no XML tree returned
|
|
* @retval -1 Fatal error
|
|
*/
|
|
static int
|
|
clixon_plugin_statedata_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
cvec *nsc,
|
|
char *xpath,
|
|
cxobj **xp)
|
|
{
|
|
int retval = -1;
|
|
plgstatedata_t *fn; /* Plugin statedata fn */
|
|
cxobj *x = NULL;
|
|
void *wh = NULL;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_statedata) != NULL){
|
|
if ((x = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
wh = NULL;
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (fn(h, nsc, xpath, x) < 0){
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (clixon_err_category() < 0 && !clixon_plugin_rpc_err_set(h))
|
|
clixon_log(h, LOG_WARNING, "%s: Internal error: State callback in plugin: %s returned -1 but did not make a clixon_err call",
|
|
__FUNCTION__, clixon_plugin_name_get(cp));
|
|
goto fail; /* Dont quit here on user callbacks */
|
|
}
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
}
|
|
if (xp && x){
|
|
*xp = x;
|
|
x = NULL;
|
|
}
|
|
retval = 1;
|
|
done:
|
|
if (x)
|
|
xml_free(x);
|
|
return retval;
|
|
fail:
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*! Go through all backend statedata callbacks and collect state data
|
|
*
|
|
* This is internal system call, plugin is invoked (does not call) this function
|
|
* Backend plugins can register
|
|
* @param[in] h clicon handle
|
|
* @param[in] yspec Yang spec
|
|
* @param[in] nsc Namespace context
|
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
|
* @param[in,out] xret State XML tree is merged with existing tree.
|
|
* @retval 1 OK
|
|
* @retval 0 Statedata callback failed (xret set with netconf-error)
|
|
* @retval -1 Error
|
|
* @note xret can be replaced in this function
|
|
*/
|
|
int
|
|
clixon_plugin_statedata_all(clixon_handle h,
|
|
yang_stmt *yspec,
|
|
cvec *nsc,
|
|
char *xpath,
|
|
cxobj **xret)
|
|
{
|
|
int retval = -1;
|
|
int ret;
|
|
cxobj *x = NULL;
|
|
clixon_plugin_t *cp = NULL;
|
|
cxobj *xerr = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if ((ret = clixon_plugin_statedata_one(cp, h, nsc, xpath, &x)) < 0)
|
|
goto done;
|
|
if (ret == 0){
|
|
/* error reason should be in clixon_err_reason */
|
|
if (clixon_plugin_report_err_xml(h, &xerr,
|
|
"Internal error, state callback in plugin %s returned invalid XML: %s",
|
|
clixon_plugin_name_get(cp), clixon_err_reason()) < 0)
|
|
goto done;
|
|
xml_free(*xret);
|
|
*xret = xerr;
|
|
xerr = NULL;
|
|
goto fail;
|
|
}
|
|
if (x == NULL)
|
|
continue;
|
|
if (xml_child_nr(x) == 0){
|
|
xml_free(x);
|
|
x = NULL;
|
|
continue;
|
|
}
|
|
clixon_debug_xml(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, x, "%s STATE:", clixon_plugin_name_get(cp));
|
|
/* XXX: ret == 0 invalid yang binding should be handled as internal error */
|
|
if ((ret = xml_bind_yang(h, x, YB_MODULE, yspec, &xerr)) < 0)
|
|
goto done;
|
|
if (ret == 0){
|
|
if (clixon_netconf_internal_error(xerr,
|
|
". Internal error, state callback returned invalid XML from plugin: ",
|
|
clixon_plugin_name_get(cp)) < 0)
|
|
goto done;
|
|
xml_free(*xret);
|
|
*xret = xerr;
|
|
xerr = NULL;
|
|
goto fail;
|
|
}
|
|
if (xml_sort_recurse(x) < 0)
|
|
goto done;
|
|
/* Remove global defaults and empty non-presence containers */
|
|
/* XXX: only for state data and according to with-defaults setting */
|
|
if (xml_default_nopresence(x, 2, 0) < 0)
|
|
goto done;
|
|
if (xpath_first(x, nsc, "%s", xpath) != NULL){
|
|
if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
|
|
goto done;
|
|
if (ret == 0)
|
|
goto fail;
|
|
}
|
|
if (x){
|
|
xml_free(x);
|
|
x = NULL;
|
|
}
|
|
} /* while plugin */
|
|
retval = 1;
|
|
done:
|
|
if (xerr)
|
|
xml_free(xerr);
|
|
if (x)
|
|
xml_free(x);
|
|
return retval;
|
|
fail:
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*! Lock database status has changed status
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] db Database name (eg "running")
|
|
* @param[in] lock Lock status: 0: unlocked, 1: locked
|
|
* @param[in] id Session id (of locker/unlocker)
|
|
* @retval 0 OK
|
|
* @retval -1 Fatal error
|
|
*/
|
|
static int
|
|
clixon_plugin_lockdb_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
char *db,
|
|
int lock,
|
|
int id)
|
|
{
|
|
int retval = -1;
|
|
plglockdb_t *fn; /* Plugin statedata fn */
|
|
void *wh = NULL;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_lockdb) != NULL){
|
|
wh = NULL;
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
if (fn(h, db, lock, id) < 0)
|
|
goto done;
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Lock database status has changed status
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] db Database name (eg "running")
|
|
* @param[in] lock Lock status: 0: unlocked, 1: locked
|
|
* @param[in] id Session id (of locker/unlocker)
|
|
* @retval 0 OK
|
|
* @retval -1 Fatal error
|
|
*/
|
|
int
|
|
clixon_plugin_lockdb_all(clixon_handle h,
|
|
char *db,
|
|
int lock,
|
|
int id
|
|
)
|
|
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (clixon_plugin_lockdb_one(cp, h, db, lock, id) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Register a state data callback
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] fn Callback
|
|
* @param[in] xpath Registered XPath using canonical prefixes
|
|
* @param[in] arg Domain-specific argument to send to callback
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_pagination_cb_register(clixon_handle h,
|
|
handler_function fn,
|
|
char *xpath,
|
|
void *arg)
|
|
{
|
|
int retval = -1;
|
|
dispatcher_definition x = {xpath, fn, arg};
|
|
dispatcher_entry_t *htable = NULL;
|
|
|
|
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
|
|
if (dispatcher_register_handler(&htable, &x) < 0){
|
|
clixon_err(OE_PLUGIN, errno, "dispatcher");
|
|
goto done;
|
|
}
|
|
if (clicon_ptr_set(h, "pagination-entries", htable) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Free pagination callback structure
|
|
*
|
|
* @param[in] h Clixon handle
|
|
*/
|
|
int
|
|
clixon_pagination_free(clixon_handle h)
|
|
{
|
|
dispatcher_entry_t *htable = NULL;
|
|
|
|
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
|
|
if (htable)
|
|
dispatcher_free(htable);
|
|
return 0;
|
|
}
|
|
|
|
/*! Create and initialize a validate/commit transaction
|
|
*
|
|
* @retval td New alloced transaction,
|
|
* @retval NULL Error
|
|
* @see transaction_free which deallocates the returned handle
|
|
*/
|
|
transaction_data_t *
|
|
transaction_new(void)
|
|
{
|
|
transaction_data_t *td;
|
|
static uint64_t id = 0; /* Global transaction id */
|
|
|
|
if ((td = malloc(sizeof(*td))) == NULL){
|
|
clixon_err(OE_CFG, errno, "malloc");
|
|
return NULL;
|
|
}
|
|
memset(td, 0, sizeof(*td));
|
|
td->td_id = id++;
|
|
return td;
|
|
}
|
|
|
|
/*! Free transaction structure
|
|
*
|
|
* @param[in] td Transaction data will be deallocated after the call
|
|
*/
|
|
int
|
|
transaction_free(transaction_data_t *td)
|
|
{
|
|
return transaction_free1(td, 1);
|
|
}
|
|
|
|
/*! Free transaction structure
|
|
*
|
|
* @param[in] td Transaction data will be deallocated after the call
|
|
* @param[in] copy 0: XML trees are no-copy, clear and dont free, 1: free XML trees
|
|
*/
|
|
int
|
|
transaction_free1(transaction_data_t *td,
|
|
int copy)
|
|
{
|
|
if (td->td_src){
|
|
if (copy)
|
|
xml_free(td->td_src);
|
|
else
|
|
xml_apply(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
|
(void*)(XML_FLAG_NONE|XML_FLAG_ADD|XML_FLAG_DEL|XML_FLAG_CHANGE|XML_FLAG_SKIP|XML_FLAG_MARK));
|
|
}
|
|
if (td->td_target){
|
|
if (copy)
|
|
xml_free(td->td_target);
|
|
else
|
|
xml_apply(td->td_target, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
|
(void*)(XML_FLAG_NONE|XML_FLAG_ADD|XML_FLAG_DEL|XML_FLAG_CHANGE|XML_FLAG_SKIP|XML_FLAG_MARK));
|
|
}
|
|
if (td->td_dvec)
|
|
free(td->td_dvec);
|
|
if (td->td_avec)
|
|
free(td->td_avec);
|
|
if (td->td_scvec)
|
|
free(td->td_scvec);
|
|
if (td->td_tcvec)
|
|
free(td->td_tcvec);
|
|
free(td);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
plugin_transaction_call_one(clixon_handle h,
|
|
clixon_plugin_t *cp,
|
|
trans_cb_t *fn,
|
|
const char *fnname,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
int rv;
|
|
void *wh = NULL;
|
|
|
|
wh = NULL;
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), fnname) < 0)
|
|
goto done;
|
|
rv = fn(h, (transaction_data)td);
|
|
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), fnname) < 0)
|
|
goto done;
|
|
if (rv < 0) {
|
|
if (!clixon_plugin_rpc_err_set(h) && !clixon_err_category())
|
|
/* sanity: log if err is not called ! */
|
|
clixon_log(h, LOG_NOTICE, "%s: Plugin '%s' callback does not make clixon_err or clixon_plugin_rpc_err call on error",
|
|
fnname, clixon_plugin_name_get(cp));
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single plugin transaction_begin() before a validate/commit.
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_begin_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_begin) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/* The plugin_transaction routines need access to struct plugin which is local to this file */
|
|
|
|
/*! Call transaction_begin() in all plugins before a validate/commit.
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error: one of the plugin callbacks returned error
|
|
*/
|
|
int
|
|
plugin_transaction_begin_all(clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (plugin_transaction_begin_one(cp, h, td) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single plugin transaction_validate() in a validate/commit transaction
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_validate_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_validate) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/*! Call transaction_validate callbacks in all backend plugins
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK. Validation succeeded in all plugins
|
|
* @retval -1 Error: one of the plugin callbacks returned validation fail
|
|
*/
|
|
int
|
|
plugin_transaction_validate_all(clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (plugin_transaction_validate_one(cp, h, td) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single plugin transaction_complete() in a validate/commit transaction
|
|
*
|
|
* complete is called after validate (before commit)
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_complete_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_complete) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/*! Call transaction_complete() in all plugins after validation (before commit)
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error: one of the plugin callbacks returned error
|
|
* @note Call plugins which have commit dependencies?
|
|
* @note Rename to transaction_complete?
|
|
*/
|
|
int
|
|
plugin_transaction_complete_all(clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (plugin_transaction_complete_one(cp, h, td) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Revert a commit
|
|
*
|
|
* @param[in] h CLICON handle
|
|
* @param[in] td Transaction data
|
|
* @param[in] nr The plugin where an error occured.
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* The revert is made in plugin before this one. Eg if error occurred in
|
|
* plugin 2, then the revert will be made in plugins 1 and 0.
|
|
*/
|
|
static int
|
|
plugin_transaction_revert_all(clixon_handle h,
|
|
transaction_data_t *td,
|
|
int nr)
|
|
{
|
|
int retval = 0;
|
|
clixon_plugin_t *cp = NULL;
|
|
trans_cb_t *fn;
|
|
|
|
while ((cp = clixon_plugin_each_revert(h, cp, nr)) != NULL) {
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_revert) == NULL)
|
|
continue;
|
|
|
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
|
clixon_log(h, LOG_NOTICE, "%s: Plugin '%s' trans_revert callback failed",
|
|
__FUNCTION__, clixon_plugin_name_get(cp));
|
|
break;
|
|
}
|
|
}
|
|
return retval; /* ignore errors */
|
|
}
|
|
|
|
/*! Revert a commit for failed plugin
|
|
* The commit failed is called for only failed plugin before revert all cb.
|
|
*/
|
|
static int
|
|
plugin_transaction_commit_failed(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_commit_failed) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/*! Call single plugin transaction_commit() in a commit transaction
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_commit_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_commit) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/*! Call transaction_commit callbacks in all backend plugins
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error: one of the plugin callbacks returned error
|
|
* If any of the commit callbacks fail by returning -1, a revert of the
|
|
* transaction is tried by calling the commit callbacsk with reverse arguments
|
|
* and in reverse order.
|
|
*/
|
|
int
|
|
plugin_transaction_commit_all(clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
int i=0;
|
|
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
i++;
|
|
if (plugin_transaction_commit_one(cp, h, td) < 0){
|
|
/* First make an effort ro revert transaction for the failed plugin */
|
|
plugin_transaction_commit_failed(cp, h, td);
|
|
/* Make an effort to revert transaction */
|
|
plugin_transaction_revert_all(h, td, i-1);
|
|
goto done;
|
|
}
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single plugin transaction_commit_done() in a commit transaction
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_commit_done_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_commit_done) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/*! Call transaction_commit_done callbacks in all backend plugins
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error: one of the plugin callbacks returned error
|
|
* @note no revert is done
|
|
*/
|
|
int
|
|
plugin_transaction_commit_done_all(clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (plugin_transaction_commit_done_one(cp, h, td) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Call single plugin transaction_end() in a commit/validate transaction
|
|
*
|
|
* @param[in] cp Plugin handle
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_end_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_end) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/*! Call transaction_end() in all plugins after a successful commit.
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_end_all(clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (plugin_transaction_end_one(cp, h, td) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
plugin_transaction_abort_one(clixon_plugin_t *cp,
|
|
clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
trans_cb_t *fn;
|
|
|
|
if ((fn = clixon_plugin_api_get(cp)->ca_trans_abort) != NULL)
|
|
return plugin_transaction_call_one(h, cp, fn, __FUNCTION__, td);
|
|
return 0;
|
|
}
|
|
|
|
/*! Call transaction_abort() in all plugins after a failed validation/commit.
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] td Transaction data
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
plugin_transaction_abort_all(clixon_handle h,
|
|
transaction_data_t *td)
|
|
{
|
|
int retval = -1;
|
|
clixon_plugin_t *cp = NULL;
|
|
|
|
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
|
|
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
|
|
if (plugin_transaction_abort_one(cp, h, td) < 0)
|
|
; /* dont abort on error */
|
|
}
|
|
retval = 0;
|
|
return retval;
|
|
}
|