diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index d46456cbc2..7e709770e0 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -1,427 +1,428 @@ /* * Copyright 2004-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Maximum number of diffs to ignore while waiting for a resync */ #define MAX_DIFF_RETRY 5 bool based_is_primary = false; xmlNode *the_cib = NULL; int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { const char *host = crm_element_value(req, F_ORIG); *answer = NULL; if (crm_element_value(req, F_CIB_ISREPLY) == NULL) { crm_info("Peer %s is requesting to shut down", host); return pcmk_ok; } if (cib_shutdown_flag == FALSE) { crm_err("Peer %s mistakenly thinks we wanted to shut down", host); return -EINVAL; } crm_info("Peer %s has acknowledged our shutdown request", host); terminate_cib(__func__, 0); return pcmk_ok; } int cib_process_default(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { int result = pcmk_ok; crm_trace("Processing \"%s\" event", op); *answer = NULL; if (op == NULL) { result = -EINVAL; crm_err("No operation specified"); } else if (strcmp(PCMK__CIB_REQUEST_NOOP, op) != 0) { result = -EPROTONOSUPPORT; crm_err("Action [%s] is not supported by the CIB manager", op); } return result; } int cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { int result = pcmk_ok; crm_trace("Processing \"%s\" event", op); if (pcmk__str_eq(op, PCMK__CIB_REQUEST_IS_PRIMARY, pcmk__str_none)) { if (based_is_primary) { result = pcmk_ok; } else { result = -EPERM; } return result; } if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) { if (!based_is_primary) { crm_info("We are now in R/W mode"); based_is_primary = true; } else { crm_debug("We are still in R/W mode"); } } else if (based_is_primary) { crm_info("We are now in R/O mode"); based_is_primary = false; } return result; } /* Set to 1 when a sync is requested, incremented when a diff is ignored, * reset to 0 when a sync is received */ static int sync_in_progress = 0; void send_sync_request(const char *host) { xmlNode *sync_me = create_xml_node(NULL, "sync-me"); crm_info("Requesting re-sync from %s", (host? host : "all peers")); sync_in_progress = 1; crm_xml_add(sync_me, F_TYPE, "cib"); crm_xml_add(sync_me, F_CIB_OPERATION, PCMK__CIB_REQUEST_SYNC_TO_ONE); crm_xml_add(sync_me, F_CIB_DELEGATED, stand_alone? "localhost" : crm_cluster->uname); send_cluster_message(host ? crm_get_peer(0, host) : NULL, crm_msg_cib, sync_me, FALSE); free_xml(sync_me); } int cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { const char *host = crm_element_value(req, F_ORIG); const char *seq = crm_element_value(req, F_CIB_PING_ID); char *digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET); crm_trace("Processing \"%s\" event %s from %s", op, seq, host); *answer = create_xml_node(NULL, XML_CRM_TAG_PING); crm_xml_add(*answer, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); crm_xml_add(*answer, XML_ATTR_DIGEST, digest); crm_xml_add(*answer, F_CIB_PING_ID, seq); pcmk__if_tracing( { // Append additional detail so the receiver can log the differences add_message_xml(*answer, F_CIB_CALLDATA, the_cib); }, { // Always include at least the version details const char *tag = TYPE(the_cib); xmlNode *shallow = create_xml_node(NULL, tag); copy_in_properties(shallow, the_cib); add_message_xml(*answer, F_CIB_CALLDATA, shallow); free_xml(shallow); } ); crm_info("Reporting our current digest to %s: %s for %s.%s.%s", host, digest, crm_element_value(existing_cib, XML_ATTR_GENERATION_ADMIN), crm_element_value(existing_cib, XML_ATTR_GENERATION), crm_element_value(existing_cib, XML_ATTR_NUMUPDATES)); free(digest); return pcmk_ok; } int cib_process_sync(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { return sync_our_cib(req, TRUE); } int cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { int rc = pcmk_ok; *answer = NULL; if(crm_element_value(req, F_CIB_SCHEMA_MAX)) { /* The originator of an upgrade request sends it to the DC, without * F_CIB_SCHEMA_MAX. If an upgrade is needed, the DC re-broadcasts the * request with F_CIB_SCHEMA_MAX, and each node performs the upgrade * (and notifies its local clients) here. */ return cib_process_upgrade( op, options, section, req, input, existing_cib, result_cib, answer); } else { int new_version = 0; int current_version = 0; xmlNode *scratch = copy_xml(existing_cib); const char *host = crm_element_value(req, F_ORIG); const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION); const char *client_id = crm_element_value(req, F_CIB_CLIENTID); const char *call_opts = crm_element_value(req, F_CIB_CALLOPTS); const char *call_id = crm_element_value(req, F_CIB_CALLID); crm_trace("Processing \"%s\" event", op); if (value != NULL) { current_version = get_schema_version(value); } rc = update_validation(&scratch, &new_version, 0, TRUE, TRUE); if (new_version > current_version) { xmlNode *up = create_xml_node(NULL, __func__); rc = pcmk_ok; crm_notice("Upgrade request from %s verified", host); crm_xml_add(up, F_TYPE, "cib"); crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE); crm_xml_add(up, F_CIB_SCHEMA_MAX, get_schema_name(new_version)); crm_xml_add(up, F_CIB_DELEGATED, host); crm_xml_add(up, F_CIB_CLIENTID, client_id); crm_xml_add(up, F_CIB_CALLOPTS, call_opts); crm_xml_add(up, F_CIB_CALLID, call_id); if (cib_legacy_mode() && based_is_primary) { rc = cib_process_upgrade( op, options, section, up, input, existing_cib, result_cib, answer); } else { send_cluster_message(NULL, crm_msg_cib, up, FALSE); } free_xml(up); } else if(rc == pcmk_ok) { rc = -pcmk_err_schema_unchanged; } if (rc != pcmk_ok) { // Notify originating peer so it can notify its local clients crm_node_t *origin = pcmk__search_cluster_node_cache(0, host); crm_info("Rejecting upgrade request from %s: %s " CRM_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc, (origin? origin->uname : "lost")); if (origin) { xmlNode *up = create_xml_node(NULL, __func__); crm_xml_add(up, F_TYPE, "cib"); crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE); crm_xml_add(up, F_CIB_DELEGATED, host); crm_xml_add(up, F_CIB_ISREPLY, host); crm_xml_add(up, F_CIB_CLIENTID, client_id); crm_xml_add(up, F_CIB_CALLOPTS, call_opts); crm_xml_add(up, F_CIB_CALLID, call_id); crm_xml_add_int(up, F_CIB_UPGRADE_RC, rc); if (send_cluster_message(origin, crm_msg_cib, up, TRUE) == FALSE) { crm_warn("Could not send CIB upgrade result to %s", host); } free_xml(up); } } free_xml(scratch); } return rc; } int cib_process_sync_one(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { return sync_our_cib(req, FALSE); } int cib_server_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { int rc = pcmk_ok; if (sync_in_progress > MAX_DIFF_RETRY) { /* Don't ignore diffs forever; the last request may have been lost. * If the diff fails, we'll ask for another full resync. */ sync_in_progress = 0; } // The primary instance should never ignore a diff if (sync_in_progress && !based_is_primary) { int diff_add_updates = 0; int diff_add_epoch = 0; int diff_add_admin_epoch = 0; int diff_del_updates = 0; int diff_del_epoch = 0; int diff_del_admin_epoch = 0; cib_diff_version_details(input, &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); sync_in_progress++; crm_notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)", diff_del_admin_epoch, diff_del_epoch, diff_del_updates, diff_add_admin_epoch, diff_add_epoch, diff_add_updates); return -pcmk_err_diff_resync; } rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer); crm_trace("result: %s (%d), %s", pcmk_strerror(rc), rc, (based_is_primary? "primary": "secondary")); if ((rc == -pcmk_err_diff_resync) && !based_is_primary) { free_xml(*result_cib); *result_cib = NULL; send_sync_request(NULL); } else if (rc == -pcmk_err_diff_resync) { rc = -pcmk_err_diff_failed; if (options & cib_force_diff) { crm_warn("Not requesting full refresh in R/W mode"); } } else if ((rc != pcmk_ok) && !based_is_primary && cib_legacy_mode()) { crm_warn("Requesting full CIB refresh because update failed: %s" CRM_XS " rc=%d", pcmk_strerror(rc), rc); pcmk__output_set_log_level(logger_out, LOG_INFO); logger_out->message(logger_out, "xml-patchset", input); free_xml(*result_cib); *result_cib = NULL; send_sync_request(NULL); } return rc; } int cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { const char *tag = crm_element_name(input); int rc = cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer); if (rc == pcmk_ok && pcmk__str_eq(tag, XML_TAG_CIB, pcmk__str_casei)) { sync_in_progress = 0; } return rc; } +// @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed int cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { return -EINVAL; } int sync_our_cib(xmlNode * request, gboolean all) { int result = pcmk_ok; char *digest = NULL; const char *host = crm_element_value(request, F_ORIG); const char *op = crm_element_value(request, F_CIB_OPERATION); xmlNode *replace_request = NULL; CRM_CHECK(the_cib != NULL, return -EINVAL); replace_request = cib_msg_copy(request, FALSE); CRM_CHECK(replace_request != NULL, return -EINVAL); crm_debug("Syncing CIB to %s", all ? "all peers" : host); if (all == FALSE && host == NULL) { crm_log_xml_err(request, "bad sync"); } /* remove the "all == FALSE" condition * * sync_from was failing, the local client wasn't being notified * because it didn't know it was a reply * setting this does not prevent the other nodes from applying it * if all == TRUE */ if (host != NULL) { crm_xml_add(replace_request, F_CIB_ISREPLY, host); } if (all) { xml_remove_prop(replace_request, F_CIB_HOST); } crm_xml_add(replace_request, F_CIB_OPERATION, PCMK__CIB_REQUEST_REPLACE); crm_xml_add(replace_request, "original_" F_CIB_OPERATION, op); pcmk__xe_set_bool_attr(replace_request, F_CIB_GLOBAL_UPDATE, true); crm_xml_add(replace_request, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET); crm_xml_add(replace_request, XML_ATTR_DIGEST, digest); add_message_xml(replace_request, F_CIB_CALLDATA, the_cib); if (send_cluster_message (all ? NULL : crm_get_peer(0, host), crm_msg_cib, replace_request, FALSE) == FALSE) { result = -ENOTCONN; } free_xml(replace_request); free(digest); return result; } diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 2d179e0110..2005ef51de 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.c @@ -1,750 +1,751 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2023 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 #include #include #include #include #include #include #include #include #include #include #include #include #include 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 = calloc(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 = calloc(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 = calloc(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 = NULL; async_timer = calloc(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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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, NULL); } 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; } 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; 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; 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); }