/* * Copyright 2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * Licensed under the GNU General Public License version 2 or later (GPLv2+). */ /* * Intended demo use case: * * - as root, start corosync * - start "./based -N"; hint: * su -s /bin/sh -c './based -N' hacluster * - start pacemaker-fenced; hint: * su -c 'env PCMK_logpriority=crit ../../daemons/fenced/pacemaker-fenced' * - wait a bit (5 < seconds < 20) * - as haclient group (or root), run "stonith admin --list-registered" * - observe whether such invocation is blocked or not */ #include /* printf, perror */ #include "crm/cib.h" /* cib_zero_copy */ #include "crm/cib/internal.h" /* CIB_OP_CREATE */ #include "crm/msg_xml.h" /* F_SUBTYPE */ #include "daemons/based/pacemaker-based.h" /* cib_notify_diff */ #include "based.h" #define OPTCHAR 'N' static size_t module_handle; struct cib_notification_s { xmlNode *msg; struct iovec *iov; int32_t iov_size; }; /* see based/based_notify.c:cib_notify_send_one */ static bool mock_based_cib_notify_send_one(crm_client_t *client, xmlNode *xml) { const char *type = NULL; bool do_send = false; struct iovec *iov; ssize_t rc = crm_ipc_prepare(0, xml, &iov, 0); struct cib_notification_s update = { .msg = xml, .iov = iov, .iov_size = rc, }; CRM_CHECK(client != NULL, return true); if (client->ipcs == NULL && client->remote == NULL) { crm_warn("Skipping client with NULL channel"); return FALSE; } type = crm_element_value(update.msg, F_SUBTYPE); CRM_LOG_ASSERT(type != NULL); if (is_set(client->options, cib_notify_diff) && safe_str_eq(type, T_CIB_DIFF_NOTIFY)) { if (crm_ipcs_sendv(client, update.iov, crm_ipc_server_event) < 0) crm_warn("Notification of client %s/%s failed", client->name, client->id); } pcmk_free_ipc_event(iov); return FALSE; } /* see based/based_notify.c:do_cib_notify + cib_notify_send */ void do_cib_notify(crm_client_t *cib_client, int options, const char *op, xmlNode *update, int result, xmlNode *result_data, const char *msg_type) { xmlNode *update_msg = NULL; const char *id = NULL; update_msg = create_xml_node(NULL, "notify"); crm_xml_add(update_msg, F_TYPE, T_CIB_NOTIFY); crm_xml_add(update_msg, F_SUBTYPE, msg_type); crm_xml_add(update_msg, F_CIB_OPERATION, op); crm_xml_add_int(update_msg, F_CIB_RC, result); if (result_data != NULL) { id = crm_element_value(result_data, XML_ATTR_ID); if (id != NULL) crm_xml_add(update_msg, F_CIB_OBJID, id); } if (update != NULL) { crm_trace("Setting type to update->name: %s", crm_element_name(update)); crm_xml_add(update_msg, F_CIB_OBJTYPE, crm_element_name(update)); } else if (result_data != NULL) { crm_trace("Setting type to new_obj->name: %s", crm_element_name(result_data)); crm_xml_add(update_msg, F_CIB_OBJTYPE, crm_element_name(result_data)); } else { crm_trace("Not Setting type"); } #if 0 attach_cib_generation(update_msg, "cib_generation", the_cib); #endif if (update != NULL) { add_message_xml(update_msg, F_CIB_UPDATE, update); } if (result_data != NULL) { add_message_xml(update_msg, F_CIB_UPDATE_RESULT, result_data); } mock_based_cib_notify_send_one(cib_client, update_msg); free_xml(update_msg); } static gboolean mock_based_notifyfencedmer_callback_worker(gpointer data) { crm_client_t *cib_client = (crm_client_t *) data; xmlNode *result_data; xmlNode *input, *update; int options; char update_str[4096]; options |= cib_zero_copy; input = create_xml_node(NULL, "cib"); /* spam it */ #if 0 for (size_t i = 0; i < SIZE_MAX - 1; i++) { #else for (size_t i = 0; i < 10000; i++) { #endif /* NOTE: we need to trigger fenced attention, add new fence device */ snprintf(update_str, sizeof(update_str), "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n", i, i+1); update = xmlReadMemory(update_str, sizeof(update_str), "file:///tmp/update", NULL, 0)->children; do_cib_notify(cib_client, options, CIB_OP_CREATE, input, pcmk_ok, update, T_CIB_DIFF_NOTIFY); free_xml(update); }; free_xml(input); } static void mock_based_notifyfenced_cib_notify_hook(crm_client_t *cib_client) { /* MOCK: client asked for upcoming diff's, let's spam it a bit after a while... */ crm_info("Going to spam %s (%s) in 5 seconds...", cib_client->name, cib_client->id); mainloop_timer_start(mainloop_timer_add("spammer", 5000, FALSE, mock_based_notifyfencedmer_callback_worker, cib_client)); } /* * */ static int mock_based_notifyfenced_argparse_hook(struct mock_based_context_s *ctxt, bool usage, int argc_to_go, const char *argv_to_go[]) { const char *opt = *argv_to_go; restart: switch(*opt) { case '-': if (opt == *argv_to_go) { opt++; goto restart; } break; case OPTCHAR: if (usage) { printf("spam the \"cib diff\" notification client" " (targeting pacemaker-fenced in particular)\n"); } else { #if 0 ctxt->modules[module_handle]->priv = malloc(sizeof(mock_based_notifyfenced_priv_t)); if (ctxt->modules[module_handle]->priv == NULL) { perror("malloc"); return -1; } #endif } return 1; } return 0; } #if 0 static void mock_based_notifyfenced_destroy_hook(module_t *mod) { free(mod->priv); } #endif __attribute__((__constructor__)) void mock_based_notifyfenced_init(void) { module_handle = mock_based_register_module((module_t){ .shortopt = OPTCHAR, .hooks = { .argparse = mock_based_notifyfenced_argparse_hook, //.destroy = mock_based_notifyfenced_destroy_hook, /* specialized hooks */ .cib_notify = mock_based_notifyfenced_cib_notify_hook, } }); }