Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F7631344
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
59 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c
index c98042915e..40ede8e713 100644
--- a/lib/cib/cib_client.c
+++ b/lib/cib/cib_client.c
@@ -1,841 +1,823 @@
/*
* Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/common/xml.h>
static GHashTable *cib_op_callback_table = NULL;
-#define op_common(cib) do { \
- if(cib == NULL) { \
- return -EINVAL; \
- } else if(cib->delegate_fn == NULL) { \
- return -EPROTONOSUPPORT; \
- } \
- } while(0)
-
static int
cib_client_set_op_callback(cib_t *cib,
void (*callback) (const xmlNode * msg, int call_id,
int rc, xmlNode * output))
{
if (callback == NULL) {
crm_info("Un-Setting operation callback");
} else {
crm_trace("Setting operation callback");
}
cib->op_callback = callback;
return pcmk_ok;
}
static gint
ciblib_GCompareFunc(gconstpointer a, gconstpointer b)
{
int rc = 0;
const cib_notify_client_t *a_client = a;
const cib_notify_client_t *b_client = b;
CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
rc = strcmp(a_client->event, b_client->event);
if (rc == 0) {
if (a_client->callback == b_client->callback) {
return 0;
} else if (((long)a_client->callback) < ((long)b_client->callback)) {
crm_trace("callbacks for %s are not equal: %p < %p",
a_client->event, a_client->callback, b_client->callback);
return -1;
}
crm_trace("callbacks for %s are not equal: %p > %p",
a_client->event, a_client->callback, b_client->callback);
return 1;
}
return rc;
}
static int
cib_client_add_notify_callback(cib_t * cib, const char *event,
void (*callback) (const char *event,
xmlNode * msg))
{
GList *list_item = NULL;
cib_notify_client_t *new_client = NULL;
if ((cib->variant != cib_native) && (cib->variant != cib_remote)) {
return -EPROTONOSUPPORT;
}
crm_trace("Adding callback for %s events (%d)",
event, g_list_length(cib->notify_list));
new_client = pcmk__assert_alloc(1, sizeof(cib_notify_client_t));
new_client->event = event;
new_client->callback = callback;
list_item = g_list_find_custom(cib->notify_list, new_client,
ciblib_GCompareFunc);
if (list_item != NULL) {
crm_warn("Callback already present");
free(new_client);
return -EINVAL;
} else {
cib->notify_list = g_list_append(cib->notify_list, new_client);
cib->cmds->register_notification(cib, event, 1);
crm_trace("Callback added (%d)", g_list_length(cib->notify_list));
}
return pcmk_ok;
}
static int
get_notify_list_event_count(cib_t *cib, const char *event)
{
int count = 0;
for (GList *iter = g_list_first(cib->notify_list); iter != NULL;
iter = iter->next) {
cib_notify_client_t *client = (cib_notify_client_t *) iter->data;
if (strcmp(client->event, event) == 0) {
count++;
}
}
crm_trace("event(%s) count : %d", event, count);
return count;
}
static int
cib_client_del_notify_callback(cib_t *cib, const char *event,
void (*callback) (const char *event,
xmlNode *msg))
{
GList *list_item = NULL;
cib_notify_client_t *new_client = NULL;
if (cib->variant != cib_native && cib->variant != cib_remote) {
return -EPROTONOSUPPORT;
}
if (get_notify_list_event_count(cib, event) == 0) {
crm_debug("The callback of the event does not exist(%s)", event);
return pcmk_ok;
}
crm_debug("Removing callback for %s events", event);
new_client = pcmk__assert_alloc(1, sizeof(cib_notify_client_t));
new_client->event = event;
new_client->callback = callback;
list_item = g_list_find_custom(cib->notify_list, new_client, ciblib_GCompareFunc);
if (list_item != NULL) {
cib_notify_client_t *list_client = list_item->data;
cib->notify_list = g_list_remove(cib->notify_list, list_client);
free(list_client);
crm_trace("Removed callback");
} else {
crm_trace("Callback not present");
}
if (get_notify_list_event_count(cib, event) == 0) {
/* When there is not the registration of the event, the processing turns off a notice. */
cib->cmds->register_notification(cib, event, 0);
}
free(new_client);
return pcmk_ok;
}
static gboolean
cib_async_timeout_handler(gpointer data)
{
struct timer_rec_s *timer = data;
crm_debug("Async call %d timed out after %ds",
timer->call_id, timer->timeout);
cib_native_callback(timer->cib, NULL, timer->call_id, -ETIME);
// We remove the handler in remove_cib_op_callback()
return G_SOURCE_CONTINUE;
}
static gboolean
cib_client_register_callback_full(cib_t *cib, int call_id, int timeout,
gboolean only_success, void *user_data,
const char *callback_name,
void (*callback)(xmlNode *, int, int,
xmlNode *, void *),
void (*free_func)(void *))
{
cib_callback_client_t *blob = NULL;
if (call_id < 0) {
if (only_success == FALSE) {
callback(NULL, call_id, call_id, NULL, user_data);
} else {
crm_warn("CIB call failed: %s", pcmk_strerror(call_id));
}
if (user_data && free_func) {
free_func(user_data);
}
return FALSE;
}
blob = pcmk__assert_alloc(1, sizeof(cib_callback_client_t));
blob->id = callback_name;
blob->only_success = only_success;
blob->user_data = user_data;
blob->callback = callback;
blob->free_func = free_func;
if (timeout > 0) {
struct timer_rec_s *async_timer =
pcmk__assert_alloc(1, sizeof(struct timer_rec_s));
blob->timer = async_timer;
async_timer->cib = cib;
async_timer->call_id = call_id;
async_timer->timeout = timeout * 1000;
async_timer->ref = g_timeout_add(async_timer->timeout,
cib_async_timeout_handler,
async_timer);
}
crm_trace("Adding callback %s for call %d", callback_name, call_id);
pcmk__intkey_table_insert(cib_op_callback_table, call_id, blob);
return TRUE;
}
static gboolean
cib_client_register_callback(cib_t *cib, int call_id, int timeout,
gboolean only_success, void *user_data,
const char *callback_name,
void (*callback) (xmlNode *, int, int, xmlNode *,
void *))
{
return cib_client_register_callback_full(cib, call_id, timeout,
only_success, user_data,
callback_name, callback, NULL);
}
static int
cib_client_noop(cib_t * cib, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_NOOP, NULL, NULL, NULL, NULL,
call_options, cib->user);
}
static int
cib_client_ping(cib_t * cib, xmlNode ** output_data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, CRM_OP_PING, NULL, NULL, NULL, output_data,
call_options, cib->user);
}
static int
cib_client_query(cib_t * cib, const char *section, xmlNode ** output_data, int call_options)
{
return cib->cmds->query_from(cib, NULL, section, output_data, call_options);
}
static int
cib_client_query_from(cib_t * cib, const char *host, const char *section,
xmlNode ** output_data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, host, section, NULL,
output_data, call_options, cib->user);
}
static int
is_primary(cib_t *cib)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_IS_PRIMARY, NULL, NULL, NULL,
NULL, cib_scope_local|cib_sync_call, cib->user);
}
static int
set_secondary(cib_t *cib, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_SECONDARY, NULL, NULL, NULL,
NULL, call_options, cib->user);
}
static int
set_all_secondary(cib_t * cib, int call_options)
{
return -EPROTONOSUPPORT;
}
static int
set_primary(cib_t *cib, int call_options)
{
- op_common(cib);
crm_trace("Adding cib_scope_local to options");
return cib_internal_op(cib, PCMK__CIB_REQUEST_PRIMARY, NULL, NULL, NULL,
NULL, call_options|cib_scope_local, cib->user);
}
static int
cib_client_bump_epoch(cib_t * cib, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_BUMP, NULL, NULL, NULL, NULL,
call_options, cib->user);
}
static int
cib_client_upgrade(cib_t * cib, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_UPGRADE, NULL, NULL, NULL,
NULL, call_options, cib->user);
}
static int
cib_client_sync(cib_t * cib, const char *section, int call_options)
{
return cib->cmds->sync_from(cib, NULL, section, call_options);
}
static int
cib_client_sync_from(cib_t * cib, const char *host, const char *section, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_SYNC_TO_ALL, host, section,
NULL, NULL, call_options, cib->user);
}
static int
cib_client_create(cib_t * cib, const char *section, xmlNode * data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_CREATE, NULL, section, data,
NULL, call_options, cib->user);
}
static int
cib_client_modify(cib_t * cib, const char *section, xmlNode * data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_MODIFY, NULL, section, data,
NULL, call_options, cib->user);
}
static int
cib_client_replace(cib_t * cib, const char *section, xmlNode * data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_REPLACE, NULL, section, data,
NULL, call_options, cib->user);
}
static int
cib_client_delete(cib_t * cib, const char *section, xmlNode * data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_DELETE, NULL, section, data,
NULL, call_options, cib->user);
}
static int
cib_client_delete_absolute(cib_t * cib, const char *section, xmlNode * data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_ABS_DELETE, NULL, section,
data, NULL, call_options, cib->user);
}
static int
cib_client_erase(cib_t * cib, xmlNode ** output_data, int call_options)
{
- op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_ERASE, NULL, NULL, NULL,
output_data, call_options, cib->user);
}
static int
cib_client_init_transaction(cib_t *cib)
{
int rc = pcmk_rc_ok;
- op_common(cib);
+ if (cib == NULL) {
+ return -EINVAL;
+ }
if (cib->transaction != NULL) {
// A client can have at most one transaction at a time
rc = pcmk_rc_already;
}
if (rc == pcmk_rc_ok) {
cib->transaction = pcmk__xe_create(NULL, PCMK__XE_CIB_TRANSACTION);
}
if (rc != pcmk_rc_ok) {
const char *client_id = NULL;
cib->cmds->client_id(cib, NULL, &client_id);
crm_err("Failed to initialize CIB transaction for client %s: %s",
client_id, pcmk_rc_str(rc));
}
return pcmk_rc2legacy(rc);
}
static int
cib_client_end_transaction(cib_t *cib, bool commit, int call_options)
{
const char *client_id = NULL;
int rc = pcmk_ok;
- op_common(cib);
+ if (cib == NULL) {
+ return -EINVAL;
+ }
+
cib->cmds->client_id(cib, NULL, &client_id);
client_id = pcmk__s(client_id, "(unidentified)");
if (commit) {
if (cib->transaction == NULL) {
rc = pcmk_rc_no_transaction;
crm_err("Failed to commit transaction for CIB client %s: %s",
client_id, pcmk_rc_str(rc));
return pcmk_rc2legacy(rc);
}
rc = cib_internal_op(cib, PCMK__CIB_REQUEST_COMMIT_TRANSACT, NULL, NULL,
cib->transaction, NULL, call_options, cib->user);
} else {
// Discard always succeeds
if (cib->transaction != NULL) {
crm_trace("Discarded transaction for CIB client %s", client_id);
} else {
crm_trace("No transaction found for CIB client %s", client_id);
}
}
free_xml(cib->transaction);
cib->transaction = NULL;
return rc;
}
static int
cib_client_fetch_schemas(cib_t *cib, xmlNode **output_data, const char *after_ver,
int call_options)
{
xmlNode *data = pcmk__xe_create(NULL, PCMK__XA_SCHEMA);
int rc = pcmk_ok;
crm_xml_add(data, PCMK_XA_VERSION, after_ver);
rc = cib_internal_op(cib, PCMK__CIB_REQUEST_SCHEMAS, NULL, NULL, data,
output_data, call_options, NULL);
free_xml(data);
return rc;
}
static void
cib_client_set_user(cib_t *cib, const char *user)
{
pcmk__str_update(&(cib->user), user);
}
static void
cib_destroy_op_callback(gpointer data)
{
cib_callback_client_t *blob = data;
if (blob->timer && blob->timer->ref > 0) {
g_source_remove(blob->timer->ref);
}
free(blob->timer);
if (blob->user_data && blob->free_func) {
blob->free_func(blob->user_data);
}
free(blob);
}
static void
destroy_op_callback_table(void)
{
if (cib_op_callback_table != NULL) {
g_hash_table_destroy(cib_op_callback_table);
cib_op_callback_table = NULL;
}
}
char *
get_shadow_file(const char *suffix)
{
char *cib_home = NULL;
char *fullname = NULL;
char *name = crm_strdup_printf("shadow.%s", suffix);
const char *dir = getenv("CIB_shadow_dir");
if (dir == NULL) {
uid_t uid = geteuid();
struct passwd *pwent = getpwuid(uid);
const char *user = NULL;
if (pwent) {
user = pwent->pw_name;
} else {
user = getenv("USER");
crm_perror(LOG_ERR,
"Assuming %s because cannot get user details for user ID %d",
(user? user : "unprivileged user"), uid);
}
if (pcmk__strcase_any_of(user, "root", CRM_DAEMON_USER, NULL)) {
dir = CRM_CONFIG_DIR;
} else {
const char *home = NULL;
if ((home = getenv("HOME")) == NULL) {
if (pwent) {
home = pwent->pw_dir;
}
}
dir = pcmk__get_tmpdir();
if (home && home[0] == '/') {
int rc = 0;
cib_home = crm_strdup_printf("%s/.cib", home);
rc = mkdir(cib_home, 0700);
if (rc < 0 && errno != EEXIST) {
crm_perror(LOG_ERR, "Couldn't create user-specific shadow directory: %s",
cib_home);
errno = 0;
} else {
dir = cib_home;
}
}
}
}
fullname = crm_strdup_printf("%s/%s", dir, name);
free(cib_home);
free(name);
return fullname;
}
cib_t *
cib_shadow_new(const char *shadow)
{
cib_t *new_cib = NULL;
char *shadow_file = NULL;
CRM_CHECK(shadow != NULL, return NULL);
shadow_file = get_shadow_file(shadow);
new_cib = cib_file_new(shadow_file);
free(shadow_file);
return new_cib;
}
/*!
* \brief Create a new CIB connection object, ignoring any active shadow CIB
*
* Create a new live, file, or remote CIB connection object based on the values
* of CIB-related environment variables (CIB_file, CIB_port, CIB_server,
* CIB_user, and CIB_passwd). The object will not be connected.
*
* \return Newly allocated CIB connection object
* \note The CIB API does not fully support opening multiple CIB connection
* objects simultaneously, so the returned object should be treated as a
* singleton.
*/
cib_t *
cib_new_no_shadow(void)
{
const char *shadow = getenv("CIB_shadow");
cib_t *cib = NULL;
unsetenv("CIB_shadow");
cib = cib_new();
if (shadow != NULL) {
setenv("CIB_shadow", shadow, 1);
}
return cib;
}
/*!
* \brief Create a new CIB connection object
*
* Create a new live, remote, file, or shadow file CIB connection object based
* on the values of CIB-related environment variables (CIB_shadow, CIB_file,
* CIB_port, CIB_server, CIB_user, and CIB_passwd). The object will not be
* connected.
*
* \return Newly allocated CIB connection object
* \note The CIB API does not fully support opening multiple CIB connection
* objects simultaneously, so the returned object should be treated as a
* singleton.
*/
/* @TODO Ensure all APIs support multiple simultaneous CIB connection objects
* (at least cib_free_callbacks() currently does not).
*/
cib_t *
cib_new(void)
{
const char *value = getenv("CIB_shadow");
int port;
if (value && value[0] != 0) {
return cib_shadow_new(value);
}
value = getenv("CIB_file");
if (value) {
return cib_file_new(value);
}
value = getenv("CIB_port");
if (value) {
gboolean encrypted = TRUE;
const char *server = getenv("CIB_server");
const char *user = getenv("CIB_user");
const char *pass = getenv("CIB_passwd");
/* We don't ensure port is valid (>= 0) because cib_new() currently
* can't return NULL in practice, and introducing a NULL return here
* could cause core dumps that would previously just cause signon()
* failures.
*/
pcmk__scan_port(value, &port);
value = getenv("CIB_encrypted");
if (value && crm_is_true(value) == FALSE) {
crm_info("Disabling TLS");
encrypted = FALSE;
}
if (user == NULL) {
user = CRM_DAEMON_USER;
crm_info("Defaulting to user: %s", user);
}
if (server == NULL) {
server = "localhost";
crm_info("Defaulting to localhost");
}
return cib_remote_new(server, user, pass, port, encrypted);
}
return cib_native_new();
}
/*!
* \internal
* \brief Create a generic CIB connection instance
*
* \return Newly allocated and initialized cib_t instance
*
* \note This is called by each variant's cib_*_new() function before setting
* variant-specific values.
*/
cib_t *
cib_new_variant(void)
{
cib_t *new_cib = NULL;
new_cib = calloc(1, sizeof(cib_t));
if (new_cib == NULL) {
return NULL;
}
remove_cib_op_callback(0, TRUE); /* remove all */
new_cib->call_id = 1;
new_cib->variant = cib_undefined;
new_cib->type = cib_no_connection;
new_cib->state = cib_disconnected;
new_cib->op_callback = NULL;
new_cib->variant_opaque = NULL;
new_cib->notify_list = NULL;
/* the rest will get filled in by the variant constructor */
new_cib->cmds = calloc(1, sizeof(cib_api_operations_t));
if (new_cib->cmds == NULL) {
free(new_cib);
return NULL;
}
// Deprecated method
new_cib->cmds->set_op_callback = cib_client_set_op_callback;
new_cib->cmds->add_notify_callback = cib_client_add_notify_callback;
new_cib->cmds->del_notify_callback = cib_client_del_notify_callback;
new_cib->cmds->register_callback = cib_client_register_callback;
new_cib->cmds->register_callback_full = cib_client_register_callback_full;
new_cib->cmds->noop = cib_client_noop; // Deprecated method
new_cib->cmds->ping = cib_client_ping;
new_cib->cmds->query = cib_client_query;
new_cib->cmds->sync = cib_client_sync;
new_cib->cmds->query_from = cib_client_query_from;
new_cib->cmds->sync_from = cib_client_sync_from;
new_cib->cmds->is_master = is_primary; // Deprecated method
new_cib->cmds->set_primary = set_primary;
new_cib->cmds->set_master = set_primary; // Deprecated method
new_cib->cmds->set_secondary = set_secondary;
new_cib->cmds->set_slave = set_secondary; // Deprecated method
new_cib->cmds->set_slave_all = set_all_secondary; // Deprecated method
new_cib->cmds->upgrade = cib_client_upgrade;
new_cib->cmds->bump_epoch = cib_client_bump_epoch;
new_cib->cmds->create = cib_client_create;
new_cib->cmds->modify = cib_client_modify;
new_cib->cmds->update = cib_client_modify; // Deprecated method
new_cib->cmds->replace = cib_client_replace;
new_cib->cmds->remove = cib_client_delete;
new_cib->cmds->erase = cib_client_erase;
// Deprecated method
new_cib->cmds->delete_absolute = cib_client_delete_absolute;
new_cib->cmds->init_transaction = cib_client_init_transaction;
new_cib->cmds->end_transaction = cib_client_end_transaction;
new_cib->cmds->set_user = cib_client_set_user;
new_cib->cmds->fetch_schemas = cib_client_fetch_schemas;
return new_cib;
}
void
cib_free_notify(cib_t *cib)
{
if (cib) {
GList *list = cib->notify_list;
while (list != NULL) {
cib_notify_client_t *client = g_list_nth_data(list, 0);
list = g_list_remove(list, client);
free(client);
}
cib->notify_list = NULL;
}
}
/*!
* \brief Free all callbacks for a CIB connection
*
* \param[in,out] cib CIB connection to clean up
*/
void
cib_free_callbacks(cib_t *cib)
{
cib_free_notify(cib);
destroy_op_callback_table();
}
/*!
* \brief Free all memory used by CIB connection
*
* \param[in,out] cib CIB connection to delete
*/
void
cib_delete(cib_t *cib)
{
cib_free_callbacks(cib);
if (cib) {
cib->cmds->free(cib);
}
}
void
remove_cib_op_callback(int call_id, gboolean all_callbacks)
{
if (all_callbacks) {
destroy_op_callback_table();
cib_op_callback_table = pcmk__intkey_table(cib_destroy_op_callback);
} else {
pcmk__intkey_table_remove(cib_op_callback_table, call_id);
}
}
int
num_cib_op_callbacks(void)
{
if (cib_op_callback_table == NULL) {
return 0;
}
return g_hash_table_size(cib_op_callback_table);
}
static void
cib_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
{
int call = GPOINTER_TO_INT(key);
cib_callback_client_t *blob = value;
crm_debug("Call %d (%s): pending", call, pcmk__s(blob->id, "without ID"));
}
void
cib_dump_pending_callbacks(void)
{
if (cib_op_callback_table == NULL) {
return;
}
return g_hash_table_foreach(cib_op_callback_table, cib_dump_pending_op, NULL);
}
cib_callback_client_t*
cib__lookup_id (int call_id)
{
return pcmk__intkey_table_lookup(cib_op_callback_table, call_id);
}
diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c
index 34652dc97a..f868f11397 100644
--- a/lib/cib/cib_utils.c
+++ b/lib/cib/cib_utils.c
@@ -1,1126 +1,1132 @@
/*
* Original copyright 2004 International Business Machines
* Later changes copyright 2008-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/utsname.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/common/cib_internal.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/pengine/rules.h>
gboolean
cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
{
*epoch = -1;
*updates = -1;
*admin_epoch = -1;
if (cib == NULL) {
return FALSE;
} else {
crm_element_value_int(cib, PCMK_XA_EPOCH, epoch);
crm_element_value_int(cib, PCMK_XA_NUM_UPDATES, updates);
crm_element_value_int(cib, PCMK_XA_ADMIN_EPOCH, admin_epoch);
}
return TRUE;
}
gboolean
cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
int *_admin_epoch, int *_epoch, int *_updates)
{
int add[] = { 0, 0, 0 };
int del[] = { 0, 0, 0 };
xml_patch_versions(diff, add, del);
*admin_epoch = add[0];
*epoch = add[1];
*updates = add[2];
*_admin_epoch = del[0];
*_epoch = del[1];
*_updates = del[2];
return TRUE;
}
/*!
* \internal
* \brief Get the XML patchset from a CIB diff notification
*
* \param[in] msg CIB diff notification
* \param[out] patchset Where to store XML patchset
*
* \return Standard Pacemaker return code
*/
int
cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
{
int rc = pcmk_err_generic;
xmlNode *wrapper = NULL;
CRM_ASSERT(patchset != NULL);
*patchset = NULL;
if (msg == NULL) {
crm_err("CIB diff notification received with no XML");
return ENOMSG;
}
if ((crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc) != 0)
|| (rc != pcmk_ok)) {
crm_warn("Ignore failed CIB update: %s " CRM_XS " rc=%d",
pcmk_strerror(rc), rc);
crm_log_xml_debug(msg, "failed");
return pcmk_legacy2rc(rc);
}
wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, NULL, NULL);
*patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
if (*patchset == NULL) {
crm_err("CIB diff notification received with no patchset");
return ENOMSG;
}
return pcmk_rc_ok;
}
#define XPATH_DIFF_V1 "//" PCMK__XE_CIB_UPDATE_RESULT "//" PCMK__XE_DIFF_ADDED
/*!
* \internal
* \brief Check whether a given CIB element was modified in a CIB patchset (v1)
*
* \param[in] patchset CIB XML patchset
* \param[in] element XML tag of CIB element to check (\c NULL is equivalent
* to \c PCMK_XE_CIB)
*
* \return \c true if \p element was modified, or \c false otherwise
*/
static bool
element_in_patchset_v1(const xmlNode *patchset, const char *element)
{
char *xpath = crm_strdup_printf(XPATH_DIFF_V1 "//%s",
pcmk__s(element, PCMK_XE_CIB));
xmlXPathObject *xpath_obj = xpath_search(patchset, xpath);
free(xpath);
if (xpath_obj == NULL) {
return false;
}
freeXpathObject(xpath_obj);
return true;
}
/*!
* \internal
* \brief Check whether a given CIB element was modified in a CIB patchset (v2)
*
* \param[in] patchset CIB XML patchset
* \param[in] element XML tag of CIB element to check (\c NULL is equivalent
* to \c PCMK_XE_CIB). Supported values include any CIB
* element supported by \c pcmk__cib_abs_xpath_for().
*
* \return \c true if \p element was modified, or \c false otherwise
*/
static bool
element_in_patchset_v2(const xmlNode *patchset, const char *element)
{
const char *element_xpath = pcmk__cib_abs_xpath_for(element);
const char *parent_xpath = pcmk_cib_parent_name_for(element);
char *element_regex = NULL;
bool rc = false;
CRM_CHECK(element_xpath != NULL, return false); // Unsupported element
// Matches if and only if element_xpath is part of a changed path
element_regex = crm_strdup_printf("^%s(/|$)", element_xpath);
for (const xmlNode *change = pcmk__xe_first_child(patchset, PCMK_XE_CHANGE,
NULL, NULL);
change != NULL; change = pcmk__xe_next_same(change)) {
const char *op = crm_element_value(change, PCMK__XA_CIB_OP);
const char *diff_xpath = crm_element_value(change, PCMK_XA_PATH);
if (pcmk__str_eq(diff_xpath, element_regex, pcmk__str_regex)) {
// Change to an existing element
rc = true;
break;
}
if (pcmk__str_eq(op, PCMK_VALUE_CREATE, pcmk__str_none)
&& pcmk__str_eq(diff_xpath, parent_xpath, pcmk__str_none)
&& pcmk__xe_is(pcmk__xe_first_child(change, NULL, NULL, NULL),
element)) {
// Newly added element
rc = true;
break;
}
}
free(element_regex);
return rc;
}
/*!
* \internal
* \brief Check whether a given CIB element was modified in a CIB patchset
*
* \param[in] patchset CIB XML patchset
* \param[in] element XML tag of CIB element to check (\c NULL is equivalent
* to \c PCMK_XE_CIB). Supported values include any CIB
* element supported by \c pcmk__cib_abs_xpath_for().
*
* \return \c true if \p element was modified, or \c false otherwise
*/
bool
cib__element_in_patchset(const xmlNode *patchset, const char *element)
{
int format = 1;
CRM_ASSERT(patchset != NULL);
crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
switch (format) {
case 1:
return element_in_patchset_v1(patchset, element);
case 2:
return element_in_patchset_v2(patchset, element);
default:
crm_warn("Unknown patch format: %d", format);
return false;
}
}
/*!
* \brief Create XML for a new (empty) CIB
*
* \param[in] cib_epoch What to use as \c PCMK_XA_EPOCH CIB attribute
*
* \return Newly created XML for empty CIB
* \note It is the caller's responsibility to free the result with free_xml().
*/
xmlNode *
createEmptyCib(int cib_epoch)
{
xmlNode *cib_root = NULL, *config = NULL;
cib_root = pcmk__xe_create(NULL, PCMK_XE_CIB);
crm_xml_add(cib_root, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
crm_xml_add(cib_root, PCMK_XA_VALIDATE_WITH, pcmk__highest_schema_name());
crm_xml_add_int(cib_root, PCMK_XA_EPOCH, cib_epoch);
crm_xml_add_int(cib_root, PCMK_XA_NUM_UPDATES, 0);
crm_xml_add_int(cib_root, PCMK_XA_ADMIN_EPOCH, 0);
config = pcmk__xe_create(cib_root, PCMK_XE_CONFIGURATION);
pcmk__xe_create(cib_root, PCMK_XE_STATUS);
pcmk__xe_create(config, PCMK_XE_CRM_CONFIG);
pcmk__xe_create(config, PCMK_XE_NODES);
pcmk__xe_create(config, PCMK_XE_RESOURCES);
pcmk__xe_create(config, PCMK_XE_CONSTRAINTS);
#if PCMK__RESOURCE_STICKINESS_DEFAULT != 0
{
xmlNode *rsc_defaults = pcmk__xe_create(config, PCMK_XE_RSC_DEFAULTS);
xmlNode *meta = pcmk__xe_create(rsc_defaults, PCMK_XE_META_ATTRIBUTES);
xmlNode *nvpair = pcmk__xe_create(meta, PCMK_XE_NVPAIR);
crm_xml_add(meta, PCMK_XA_ID, "build-resource-defaults");
crm_xml_add(nvpair, PCMK_XA_ID, "build-" PCMK_META_RESOURCE_STICKINESS);
crm_xml_add(nvpair, PCMK_XA_NAME, PCMK_META_RESOURCE_STICKINESS);
crm_xml_add_int(nvpair, PCMK_XA_VALUE,
PCMK__RESOURCE_STICKINESS_DEFAULT);
}
#endif
return cib_root;
}
static bool
cib_acl_enabled(xmlNode *xml, const char *user)
{
bool rc = FALSE;
if(pcmk_acl_required(user)) {
const char *value = NULL;
GHashTable *options = pcmk__strkey_table(free, free);
cib_read_config(options, xml);
value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL);
rc = crm_is_true(value);
g_hash_table_destroy(options);
}
crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
return rc;
}
/*!
* \internal
* \brief Determine whether to perform operations on a scratch copy of the CIB
*
* \param[in] op CIB operation
* \param[in] section CIB section
* \param[in] call_options CIB call options
*
* \return \p true if we should make a copy of the CIB, or \p false otherwise
*/
static bool
should_copy_cib(const char *op, const char *section, int call_options)
{
if (pcmk_is_set(call_options, cib_dryrun)) {
// cib_dryrun implies a scratch copy by definition; no side effects
return true;
}
if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
/* Commit-transaction must make a copy for atomicity. We must revert to
* the original CIB if the entire transaction cannot be applied
* successfully.
*/
return true;
}
if (pcmk_is_set(call_options, cib_transaction)) {
/* If cib_transaction is set, then we're in the process of committing a
* transaction. The commit-transaction request already made a scratch
* copy, and we're accumulating changes in that copy.
*/
return false;
}
if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_none)) {
/* Copying large CIBs accounts for a huge percentage of our CIB usage,
* and this avoids some of it.
*
* @TODO: Is this safe? See discussion at
* https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
*/
return false;
}
// Default behavior is to operate on a scratch copy
return true;
}
int
cib_perform_op(cib_t *cib, const char *op, int call_options, cib__op_fn_t fn,
bool is_query, const char *section, xmlNode *req, xmlNode *input,
bool manage_counters, bool *config_changed, xmlNode **current_cib,
xmlNode **result_cib, xmlNode **diff, xmlNode **output)
{
int rc = pcmk_ok;
bool check_schema = true;
bool make_copy = true;
xmlNode *top = NULL;
xmlNode *scratch = NULL;
xmlNode *patchset_cib = NULL;
xmlNode *local_diff = NULL;
const char *user = crm_element_value(req, PCMK__XA_CIB_USER);
bool with_digest = false;
crm_trace("Begin %s%s%s op",
(pcmk_is_set(call_options, cib_dryrun)? "dry run of " : ""),
(is_query? "read-only " : ""), op);
CRM_CHECK(output != NULL, return -ENOMSG);
CRM_CHECK(current_cib != NULL, return -ENOMSG);
CRM_CHECK(result_cib != NULL, return -ENOMSG);
CRM_CHECK(config_changed != NULL, return -ENOMSG);
if(output) {
*output = NULL;
}
*result_cib = NULL;
*config_changed = false;
if (fn == NULL) {
return -EINVAL;
}
if (is_query) {
xmlNode *cib_ro = *current_cib;
xmlNode *cib_filtered = NULL;
if (cib_acl_enabled(cib_ro, user)
&& xml_acl_filtered_copy(user, *current_cib, *current_cib,
&cib_filtered)) {
if (cib_filtered == NULL) {
crm_debug("Pre-filtered the entire cib");
return -EACCES;
}
cib_ro = cib_filtered;
crm_log_xml_trace(cib_ro, "filtered");
}
rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
if(output == NULL || *output == NULL) {
/* nothing */
} else if(cib_filtered == *output) {
cib_filtered = NULL; /* Let them have this copy */
} else if (*output == *current_cib) {
/* They already know not to free it */
} else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
/* We're about to free the document of which *output is a part */
*output = pcmk__xml_copy(NULL, *output);
} else if ((*output)->doc == (*current_cib)->doc) {
/* Give them a copy they can free */
*output = pcmk__xml_copy(NULL, *output);
}
free_xml(cib_filtered);
return rc;
}
make_copy = should_copy_cib(op, section, call_options);
if (!make_copy) {
/* Conditional on v2 patch style */
scratch = *current_cib;
// Make a copy of the top-level element to store version details
top = pcmk__xe_create(NULL, (const char *) scratch->name);
pcmk__xe_copy_attrs(top, scratch, pcmk__xaf_none);
patchset_cib = top;
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
/* If scratch points to a new object now (for example, after an erase
* operation), then *current_cib should point to the same object.
*/
*current_cib = scratch;
} else {
scratch = pcmk__xml_copy(NULL, *current_cib);
patchset_cib = *current_cib;
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
rc = (*fn) (op, call_options, section, req, input, *current_cib,
&scratch, output);
if ((scratch != NULL) && !xml_tracking_changes(scratch)) {
crm_trace("Inferring changes after %s op", op);
xml_track_changes(scratch, user, *current_cib,
cib_acl_enabled(*current_cib, user));
xml_calculate_changes(*current_cib, scratch);
}
CRM_CHECK(*current_cib != scratch, return -EINVAL);
}
xml_acl_disable(scratch); /* Allow the system to make any additional changes */
if (rc == pcmk_ok && scratch == NULL) {
rc = -EINVAL;
goto done;
} else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
crm_trace("ACL rejected part or all of the proposed changes");
rc = -EACCES;
goto done;
} else if (rc != pcmk_ok) {
goto done;
}
/* If the CIB is from a file, we don't need to check that the feature set is
* supported. All we care about in that case is the schema version, which
* is checked elsewhere.
*/
if (scratch && (cib == NULL || cib->variant != cib_file)) {
const char *new_version = crm_element_value(scratch, PCMK_XA_CRM_FEATURE_SET);
rc = pcmk__check_feature_set(new_version);
if (rc != pcmk_rc_ok) {
pcmk__config_err("Discarding update with feature set '%s' greater than our own '%s'",
new_version, CRM_FEATURE_SET);
rc = pcmk_rc2legacy(rc);
goto done;
}
}
if (patchset_cib != NULL) {
int old = 0;
int new = 0;
crm_element_value_int(scratch, PCMK_XA_ADMIN_EPOCH, &new);
crm_element_value_int(patchset_cib, PCMK_XA_ADMIN_EPOCH, &old);
if (old > new) {
crm_err("%s went backwards: %d -> %d (Opts: %#x)",
PCMK_XA_ADMIN_EPOCH, old, new, call_options);
crm_log_xml_warn(req, "Bad Op");
crm_log_xml_warn(input, "Bad Data");
rc = -pcmk_err_old_data;
} else if (old == new) {
crm_element_value_int(scratch, PCMK_XA_EPOCH, &new);
crm_element_value_int(patchset_cib, PCMK_XA_EPOCH, &old);
if (old > new) {
crm_err("%s went backwards: %d -> %d (Opts: %#x)",
PCMK_XA_EPOCH, old, new, call_options);
crm_log_xml_warn(req, "Bad Op");
crm_log_xml_warn(input, "Bad Data");
rc = -pcmk_err_old_data;
}
}
}
crm_trace("Massaging CIB contents");
pcmk__strip_xml_text(scratch);
if (!make_copy) {
/* At this point, patchset_cib is just the PCMK_XE_CIB tag and its
* properties.
*
* The v1 format would barf on this, but we know the v2 patch
* format only needs it for the top-level version fields
*/
local_diff = xml_create_patchset(2, patchset_cib, scratch,
config_changed, manage_counters);
} else {
static time_t expires = 0;
time_t tm_now = time(NULL);
if (expires < tm_now) {
expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */
with_digest = true;
}
local_diff = xml_create_patchset(0, patchset_cib, scratch,
config_changed, manage_counters);
}
pcmk__log_xml_changes(LOG_TRACE, scratch);
xml_accept_changes(scratch);
if(local_diff) {
patchset_process_digest(local_diff, patchset_cib, scratch, with_digest);
pcmk__log_xml_patchset(LOG_INFO, local_diff);
crm_log_xml_trace(local_diff, "raw patch");
}
if (make_copy && (local_diff != NULL)) {
// Original to compare against doesn't exist
pcmk__if_tracing(
{
// Validate the calculated patch set
int test_rc = pcmk_ok;
int format = 1;
xmlNode *cib_copy = pcmk__xml_copy(NULL, patchset_cib);
crm_element_value_int(local_diff, PCMK_XA_FORMAT, &format);
test_rc = xml_apply_patchset(cib_copy, local_diff,
manage_counters);
if (test_rc != pcmk_ok) {
save_xml_to_file(cib_copy, "PatchApply:calculated", NULL);
save_xml_to_file(patchset_cib, "PatchApply:input", NULL);
save_xml_to_file(scratch, "PatchApply:actual", NULL);
save_xml_to_file(local_diff, "PatchApply:diff", NULL);
crm_err("v%d patchset error, patch failed to apply: %s "
"(%d)",
format, pcmk_rc_str(pcmk_legacy2rc(test_rc)),
test_rc);
}
free_xml(cib_copy);
},
{}
);
}
if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)) {
/* Throttle the amount of costly validation we perform due to status updates
* a) we don't really care whats in the status section
* b) we don't validate any of its contents at the moment anyway
*/
check_schema = false;
}
/* === scratch must not be modified after this point ===
* Exceptions, anything in:
static filter_t filter[] = {
{ 0, PCMK_XA_CRM_DEBUG_ORIGIN },
{ 0, PCMK_XA_CIB_LAST_WRITTEN },
{ 0, PCMK_XA_UPDATE_ORIGIN },
{ 0, PCMK_XA_UPDATE_CLIENT },
{ 0, PCMK_XA_UPDATE_USER },
};
*/
if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) {
const char *schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH);
pcmk__xe_add_last_written(scratch);
pcmk__warn_if_schema_deprecated(schema);
/* Make values of origin, client, and user in scratch match
* the ones in req (if the schema allows the attributes)
*/
if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") >= 0) {
const char *origin = crm_element_value(req, PCMK__XA_SRC);
const char *client = crm_element_value(req,
PCMK__XA_CIB_CLIENTNAME);
if (origin != NULL) {
crm_xml_add(scratch, PCMK_XA_UPDATE_ORIGIN, origin);
} else {
pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_ORIGIN);
}
if (client != NULL) {
crm_xml_add(scratch, PCMK_XA_UPDATE_CLIENT, user);
} else {
pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_CLIENT);
}
if (user != NULL) {
crm_xml_add(scratch, PCMK_XA_UPDATE_USER, user);
} else {
pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_USER);
}
}
}
crm_trace("Perform validation: %s", pcmk__btoa(check_schema));
if ((rc == pcmk_ok) && check_schema
&& !pcmk__configured_schema_validates(scratch)) {
const char *current_schema = crm_element_value(scratch,
PCMK_XA_VALIDATE_WITH);
crm_warn("Updated CIB does not validate against %s schema",
pcmk__s(current_schema, "unspecified"));
rc = -pcmk_err_schema_validation;
}
done:
*result_cib = scratch;
/* @TODO: This may not work correctly with !make_copy, since we don't
* keep the original CIB.
*/
if ((rc != pcmk_ok) && cib_acl_enabled(patchset_cib, user)
&& xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) {
if (*result_cib == NULL) {
crm_debug("Pre-filtered the entire cib result");
}
free_xml(scratch);
}
if(diff) {
*diff = local_diff;
} else {
free_xml(local_diff);
}
free_xml(top);
crm_trace("Done");
return rc;
}
int
cib__create_op(cib_t *cib, const char *op, const char *host,
const char *section, xmlNode *data, int call_options,
const char *user_name, const char *client_name,
xmlNode **op_msg)
{
CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO);
*op_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND);
cib->call_id++;
if (cib->call_id < 1) {
cib->call_id = 1;
}
crm_xml_add(*op_msg, PCMK__XA_T, PCMK__VALUE_CIB);
crm_xml_add(*op_msg, PCMK__XA_CIB_OP, op);
crm_xml_add(*op_msg, PCMK__XA_CIB_HOST, host);
crm_xml_add(*op_msg, PCMK__XA_CIB_SECTION, section);
crm_xml_add(*op_msg, PCMK__XA_CIB_USER, user_name);
crm_xml_add(*op_msg, PCMK__XA_CIB_CLIENTNAME, client_name);
crm_xml_add_int(*op_msg, PCMK__XA_CIB_CALLID, cib->call_id);
crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
crm_xml_add_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options);
if (data != NULL) {
xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA);
pcmk__xml_copy(wrapper, data);
}
if (pcmk_is_set(call_options, cib_inhibit_bcast)) {
CRM_CHECK(pcmk_is_set(call_options, cib_scope_local),
free_xml(*op_msg); return -EPROTO);
}
return pcmk_ok;
}
/*!
* \internal
* \brief Check whether a CIB request is supported in a transaction
*
* \param[in] request CIB request
*
* \return Standard Pacemaker return code
*/
static int
validate_transaction_request(const xmlNode *request)
{
const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
const char *host = crm_element_value(request, PCMK__XA_CIB_HOST);
const cib__operation_t *operation = NULL;
int rc = cib__get_operation(op, &operation);
if (rc != pcmk_rc_ok) {
// cib__get_operation() logs error
return rc;
}
if (!pcmk_is_set(operation->flags, cib__op_attr_transaction)) {
crm_err("Operation %s is not supported in CIB transactions", op);
return EOPNOTSUPP;
}
if (host != NULL) {
crm_err("Operation targeting a specific node (%s) is not supported in "
"a CIB transaction",
host);
return EOPNOTSUPP;
}
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Append a CIB request to a CIB transaction
*
* \param[in,out] cib CIB client whose transaction to extend
* \param[in,out] request Request to add to transaction
*
* \return Legacy Pacemaker return code
*/
int
cib__extend_transaction(cib_t *cib, xmlNode *request)
{
int rc = pcmk_rc_ok;
CRM_ASSERT((cib != NULL) && (request != NULL));
rc = validate_transaction_request(request);
if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
rc = pcmk_rc_no_transaction;
}
if (rc == pcmk_rc_ok) {
pcmk__xml_copy(cib->transaction, request);
} else {
const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
const char *client_id = NULL;
cib->cmds->client_id(cib, NULL, &client_id);
crm_err("Failed to add '%s' operation to transaction for client %s: %s",
op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
crm_log_xml_info(request, "failed");
}
return pcmk_rc2legacy(rc);
}
void
cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
{
xmlNode *output = NULL;
cib_callback_client_t *blob = NULL;
if (msg != NULL) {
xmlNode *wrapper = NULL;
crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc);
crm_element_value_int(msg, PCMK__XA_CIB_CALLID, &call_id);
wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL);
output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
}
blob = cib__lookup_id(call_id);
if (blob == NULL) {
crm_trace("No callback found for call %d", call_id);
}
if (cib == NULL) {
crm_debug("No cib object supplied");
}
if (rc == -pcmk_err_diff_resync) {
/* This is an internal value that clients do not and should not care about */
rc = pcmk_ok;
}
if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
crm_trace("Invoking callback %s for call %d",
pcmk__s(blob->id, "without ID"), call_id);
blob->callback(msg, call_id, rc, output, blob->user_data);
} else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
crm_warn("CIB command failed: %s", pcmk_strerror(rc));
crm_log_xml_debug(msg, "Failed CIB Update");
}
/* This may free user_data, so do it after the callback */
if (blob) {
remove_cib_op_callback(call_id, FALSE);
}
if (cib && cib->op_callback != NULL) {
crm_trace("Invoking global callback for call %d", call_id);
cib->op_callback(msg, call_id, rc, output);
}
crm_trace("OP callback activated for %d", call_id);
}
void
cib_native_notify(gpointer data, gpointer user_data)
{
xmlNode *msg = user_data;
cib_notify_client_t *entry = data;
const char *event = NULL;
if (msg == NULL) {
crm_warn("Skipping callback - NULL message");
return;
}
event = crm_element_value(msg, PCMK__XA_SUBT);
if (entry == NULL) {
crm_warn("Skipping callback - NULL callback client");
return;
} else if (entry->callback == NULL) {
crm_warn("Skipping callback - NULL callback");
return;
} else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
return;
}
crm_trace("Invoking callback for %p/%s event...", entry, event);
entry->callback(event, msg);
crm_trace("Callback invoked...");
}
gboolean
cib_read_config(GHashTable * options, xmlNode * current_cib)
{
xmlNode *config = NULL;
crm_time_t *now = NULL;
if (options == NULL || current_cib == NULL) {
return FALSE;
}
now = crm_time_new(NULL);
g_hash_table_remove_all(options);
config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG);
if (config) {
pe_unpack_nvpairs(current_cib, config, PCMK_XE_CLUSTER_PROPERTY_SET,
NULL, options, PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, TRUE,
now, NULL);
}
pcmk__validate_cluster_options(options);
crm_time_free(now);
return TRUE;
}
int
cib_internal_op(cib_t * cib, const char *op, const char *host,
const char *section, xmlNode * data,
xmlNode ** output_data, int call_options, const char *user_name)
{
- int (*delegate) (cib_t * cib, const char *op, const char *host,
- const char *section, xmlNode * data,
- xmlNode ** output_data, int call_options, const char *user_name) =
- cib->delegate_fn;
+ int (*delegate)(cib_t *cib, const char *op, const char *host,
+ const char *section, xmlNode *data, xmlNode **output_data,
+ int call_options, const char *user_name) = NULL;
- if(user_name == NULL) {
- user_name = getenv("CIB_user");
+ if (cib == NULL) {
+ return -EINVAL;
}
+ delegate = cib->delegate_fn;
+ if (delegate == NULL) {
+ return -EPROTONOSUPPORT;
+ }
+ if (user_name == NULL) {
+ user_name = getenv("CIB_user");
+ }
return delegate(cib, op, host, section, data, output_data, call_options, user_name);
}
/*!
* \brief Apply a CIB update patch to a given CIB
*
* \param[in] event CIB update patch
* \param[in] input CIB to patch
* \param[out] output Resulting CIB after patch
* \param[in] level Log the patch at this log level (unless LOG_CRIT)
*
* \return Legacy Pacemaker return code
* \note sbd calls this function
*/
int
cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
int level)
{
int rc = pcmk_err_generic;
xmlNode *wrapper = NULL;
xmlNode *diff = NULL;
CRM_ASSERT(event);
CRM_ASSERT(input);
CRM_ASSERT(output);
crm_element_value_int(event, PCMK__XA_CIB_RC, &rc);
wrapper = pcmk__xe_first_child(event, PCMK__XE_CIB_UPDATE_RESULT, NULL,
NULL);
diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
if (rc < pcmk_ok || diff == NULL) {
return rc;
}
if (level > LOG_CRIT) {
pcmk__log_xml_patchset(level, diff);
}
if (input != NULL) {
rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
NULL);
if (rc != pcmk_ok) {
crm_debug("Update didn't apply: %s (%d) %p",
pcmk_strerror(rc), rc, *output);
if (rc == -pcmk_err_old_data) {
crm_trace("Masking error, we already have the supplied update");
return pcmk_ok;
}
free_xml(*output);
*output = NULL;
return rc;
}
}
return rc;
}
#define log_signon_query_err(out, fmt, args...) do { \
if (out != NULL) { \
out->err(out, fmt, ##args); \
} else { \
crm_err(fmt, ##args); \
} \
} while (0)
int
cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
{
int rc = pcmk_rc_ok;
cib_t *cib_conn = NULL;
CRM_ASSERT(cib_object != NULL);
if (cib == NULL) {
cib_conn = cib_new();
} else {
if (*cib == NULL) {
*cib = cib_new();
}
cib_conn = *cib;
}
if (cib_conn == NULL) {
return ENOMEM;
}
if (cib_conn->state == cib_disconnected) {
rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
rc = pcmk_legacy2rc(rc);
}
if (rc != pcmk_rc_ok) {
log_signon_query_err(out, "Could not connect to the CIB: %s",
pcmk_rc_str(rc));
goto done;
}
if (out != NULL) {
out->transient(out, "Querying CIB...");
}
rc = cib_conn->cmds->query(cib_conn, NULL, cib_object,
cib_scope_local|cib_sync_call);
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
}
done:
if (cib == NULL) {
cib__clean_up_connection(&cib_conn);
}
if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
return pcmk_rc_no_input;
}
return rc;
}
int
cib__signon_attempts(cib_t *cib, const char *name, enum cib_conn_type type,
int attempts)
{
int rc = pcmk_rc_ok;
crm_trace("Attempting connection to CIB manager (up to %d time%s)",
attempts, pcmk__plural_s(attempts));
for (int remaining = attempts - 1; remaining >= 0; --remaining) {
rc = cib->cmds->signon(cib, name, type);
if ((rc == pcmk_rc_ok)
|| (remaining == 0)
|| ((errno != EAGAIN) && (errno != EALREADY))) {
break;
}
// Retry after soft error (interrupted by signal, etc.)
pcmk__sleep_ms((attempts - remaining) * 500);
crm_debug("Re-attempting connection to CIB manager (%d attempt%s remaining)",
remaining, pcmk__plural_s(remaining));
}
return rc;
}
int
cib__clean_up_connection(cib_t **cib)
{
int rc;
if (*cib == NULL) {
return pcmk_rc_ok;
}
rc = (*cib)->cmds->signoff(*cib);
cib_delete(*cib);
*cib = NULL;
return pcmk_legacy2rc(rc);
}
// Deprecated functions kept only for backward API compatibility
// LCOV_EXCL_START
#include <crm/cib/util_compat.h>
xmlNode *
cib_get_generation(cib_t * cib)
{
xmlNode *the_cib = NULL;
xmlNode *generation = pcmk__xe_create(NULL, PCMK__XE_GENERATION_TUPLE);
cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call);
if (the_cib != NULL) {
pcmk__xe_copy_attrs(generation, the_cib, pcmk__xaf_none);
free_xml(the_cib);
}
return generation;
}
const char *
get_object_path(const char *object_type)
{
return pcmk_cib_xpath_for(object_type);
}
const char *
get_object_parent(const char *object_type)
{
return pcmk_cib_parent_name_for(object_type);
}
xmlNode *
get_object_root(const char *object_type, xmlNode *the_root)
{
return pcmk_find_cib_element(the_root, object_type);
}
const char *
cib_pref(GHashTable * options, const char *name)
{
return pcmk__cluster_option(options, name);
}
void
cib_metadata(void)
{
pcmk__output_t *out = NULL;
int rc = pcmk__output_new(&out, "text", NULL, NULL);
if (rc != pcmk_rc_ok) {
crm_err("Unable to output metadata: %s", pcmk_rc_str(rc));
return;
}
pcmk__daemon_metadata(out, "pacemaker-based",
"Cluster Information Base manager options",
"Cluster options used by Pacemaker's Cluster "
"Information Base manager",
pcmk__opt_based);
out->finish(out, CRM_EX_OK, true, NULL);
pcmk__output_free(out);
}
// LCOV_EXCL_STOP
// End deprecated API
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Oct 16, 2:58 PM (7 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2536395
Default Alt Text
(59 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment