Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/cib/common.c b/cib/common.c
index 03c983cab0..fd1ac81da5 100644
--- a/cib/common.c
+++ b/cib/common.c
@@ -1,348 +1,347 @@
/*
* Copyright (C) 2008 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster.h>
#include <crm/common/xml.h>
#include <cibio.h>
#include <callbacks.h>
#include <cibmessages.h>
#include "common.h"
extern gboolean cib_is_master;
extern const char *cib_root;
gboolean stand_alone = FALSE;
extern int cib_status;
extern gboolean can_write(int flags);
extern int cib_perform_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff,
gboolean privileged);
static xmlNode *
cib_prepare_common(xmlNode * root, const char *section)
{
xmlNode *data = NULL;
/* extract the CIB from the fragment */
if (root == NULL) {
return NULL;
} else if (safe_str_eq(crm_element_name(root), XML_TAG_FRAGMENT)
|| safe_str_eq(crm_element_name(root), F_CRM_DATA)
|| safe_str_eq(crm_element_name(root), F_CIB_CALLDATA)) {
data = first_named_child(root, XML_TAG_CIB);
} else {
data = root;
}
/* grab the section specified for the command */
if (section != NULL && data != NULL && crm_str_eq(crm_element_name(data), XML_TAG_CIB, TRUE)) {
data = get_object_root(section, data);
}
/* crm_log_xml_trace(root, "cib:input"); */
return data;
}
static int
cib_prepare_none(xmlNode * request, xmlNode ** data, const char **section)
{
*data = NULL;
*section = crm_element_value(request, F_CIB_SECTION);
return pcmk_ok;
}
static int
cib_prepare_data(xmlNode * request, xmlNode ** data, const char **section)
{
xmlNode *input_fragment = get_message_xml(request, F_CIB_CALLDATA);
*section = crm_element_value(request, F_CIB_SECTION);
*data = cib_prepare_common(input_fragment, *section);
/* crm_log_xml_debug(*data, "data"); */
return pcmk_ok;
}
static int
cib_prepare_sync(xmlNode * request, xmlNode ** data, const char **section)
{
*data = NULL;
*section = crm_element_value(request, F_CIB_SECTION);
return pcmk_ok;
}
static int
cib_prepare_diff(xmlNode * request, xmlNode ** data, const char **section)
{
xmlNode *input_fragment = NULL;
const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE);
*data = NULL;
*section = NULL;
if (crm_is_true(update)) {
input_fragment = get_message_xml(request, F_CIB_UPDATE_DIFF);
} else {
input_fragment = get_message_xml(request, F_CIB_CALLDATA);
}
CRM_CHECK(input_fragment != NULL, crm_log_xml_warn(request, "no input"));
*data = cib_prepare_common(input_fragment, NULL);
return pcmk_ok;
}
static int
cib_cleanup_query(int options, xmlNode ** data, xmlNode ** output)
{
CRM_LOG_ASSERT(*data == NULL);
if ((options & cib_no_children)
|| safe_str_eq(crm_element_name(*output), "xpath-query")) {
free_xml(*output);
}
return pcmk_ok;
}
static int
cib_cleanup_data(int options, xmlNode ** data, xmlNode ** output)
{
free_xml(*output);
*data = NULL;
return pcmk_ok;
}
static int
cib_cleanup_output(int options, xmlNode ** data, xmlNode ** output)
{
free_xml(*output);
return pcmk_ok;
}
static int
cib_cleanup_none(int options, xmlNode ** data, xmlNode ** output)
{
CRM_LOG_ASSERT(*data == NULL);
CRM_LOG_ASSERT(*output == NULL);
return pcmk_ok;
}
static int
cib_cleanup_sync(int options, xmlNode ** data, xmlNode ** output)
{
/* data is non-NULL but doesnt need to be free'd */
CRM_LOG_ASSERT(*data == NULL);
CRM_LOG_ASSERT(*output == NULL);
return pcmk_ok;
}
/*
typedef struct cib_operation_s
{
const char* operation;
gboolean modifies_cib;
gboolean needs_privileges;
gboolean needs_quorum;
int (*prepare)(xmlNode *, xmlNode**, const char **);
int (*cleanup)(xmlNode**, xmlNode**);
int (*fn)(
const char *, int, const char *,
xmlNode*, xmlNode*, xmlNode**, xmlNode**);
} cib_operation_t;
*/
/* technically bump does modify the cib...
* but we want to split the "bump" from the "sync"
*/
/* *INDENT-OFF* */
static cib_operation_t cib_server_ops[] = {
{NULL, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
{CIB_OP_QUERY, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_query, cib_process_query},
{CIB_OP_MODIFY, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_modify},
{CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, cib_prepare_diff, cib_cleanup_data, cib_server_process_diff},
{CIB_OP_REPLACE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_replace_svr},
{CIB_OP_CREATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_create},
{CIB_OP_DELETE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete},
{CIB_OP_SYNC, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync},
{CIB_OP_BUMP, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_bump},
{CIB_OP_ERASE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_erase},
{CRM_OP_NOOP, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
{CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete_absolute},
{CIB_OP_UPGRADE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_upgrade},
{CIB_OP_SLAVE, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_SLAVEALL, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_SYNC_ONE, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync_one},
{CIB_OP_MASTER, TRUE, TRUE, FALSE, cib_prepare_data, cib_cleanup_data, cib_process_readwrite},
{CIB_OP_ISMASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{"cib_shutdown_req",FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_shutdown_req},
- {CRM_OP_QUIT, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_quit},
{CRM_OP_PING, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_output, cib_process_ping},
};
/* *INDENT-ON* */
int
cib_get_operation_id(const char *op, int *operation)
{
static GHashTable *operation_hash = NULL;
if (operation_hash == NULL) {
int lpc = 0;
int max_msg_types = DIMOF(cib_server_ops);
operation_hash = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
for (lpc = 1; lpc < max_msg_types; lpc++) {
/* coverity[returned_null] Ignore */
int *value = malloc(sizeof(int));
*value = lpc;
g_hash_table_insert(operation_hash, (gpointer) cib_server_ops[lpc].operation, value);
}
}
if (op != NULL) {
int *value = g_hash_table_lookup(operation_hash, op);
if (value) {
*operation = *value;
return pcmk_ok;
}
}
crm_err("Operation %s is not valid", op);
*operation = -1;
return -EINVAL;
}
xmlNode *
cib_msg_copy(xmlNode * msg, gboolean with_data)
{
int lpc = 0;
const char *field = NULL;
const char *value = NULL;
xmlNode *value_struct = NULL;
static const char *field_list[] = {
F_XML_TAGNAME,
F_TYPE,
F_CIB_CLIENTID,
F_CIB_CALLOPTS,
F_CIB_CALLID,
F_CIB_OPERATION,
F_CIB_ISREPLY,
F_CIB_SECTION,
F_CIB_HOST,
F_CIB_RC,
F_CIB_DELEGATED,
F_CIB_OBJID,
F_CIB_OBJTYPE,
F_CIB_EXISTING,
F_CIB_SEENCOUNT,
F_CIB_TIMEOUT,
F_CIB_CALLBACK_TOKEN,
F_CIB_GLOBAL_UPDATE,
F_CIB_CLIENTNAME,
#if ENABLE_ACL
F_CIB_USER,
#endif
F_CIB_NOTIFY_TYPE,
F_CIB_NOTIFY_ACTIVATE
};
static const char *data_list[] = {
F_CIB_CALLDATA,
F_CIB_UPDATE,
F_CIB_UPDATE_RESULT
};
xmlNode *copy = create_xml_node(NULL, "copy");
CRM_ASSERT(copy != NULL);
for (lpc = 0; lpc < DIMOF(field_list); lpc++) {
field = field_list[lpc];
value = crm_element_value(msg, field);
if (value != NULL) {
crm_xml_add(copy, field, value);
}
}
for (lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) {
field = data_list[lpc];
value_struct = get_message_xml(msg, field);
if (value_struct != NULL) {
add_message_xml(copy, field, value_struct);
}
}
return copy;
}
cib_op_t *
cib_op_func(int call_type)
{
return &(cib_server_ops[call_type].fn);
}
gboolean
cib_op_modifies(int call_type)
{
return cib_server_ops[call_type].modifies_cib;
}
int
cib_op_can_run(int call_type, int call_options, gboolean privileged, gboolean global_update)
{
if (privileged == FALSE && cib_server_ops[call_type].needs_privileges) {
/* abort */
return -EACCES;
}
#if 0
if (rc == pcmk_ok
&& stand_alone == FALSE
&& global_update == FALSE
&& (call_options & cib_quorum_override) == 0 && cib_server_ops[call_type].needs_quorum) {
return -pcmk_err_no_quorum;
}
#endif
return pcmk_ok;
}
int
cib_op_prepare(int call_type, xmlNode * request, xmlNode ** input, const char **section)
{
return cib_server_ops[call_type].prepare(request, input, section);
}
int
cib_op_cleanup(int call_type, int options, xmlNode ** input, xmlNode ** output)
{
return cib_server_ops[call_type].cleanup(options, input, output);
}
diff --git a/cib/main.c b/cib/main.c
index 9c2654f218..d8378ae3a9 100644
--- a/cib/main.c
+++ b/cib/main.c
@@ -1,615 +1,615 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/common/xml.h>
#include <crm/common/mainloop.h>
#include <cibio.h>
#include <callbacks.h>
#include <pwd.h>
#if HAVE_LIBXML2
# include <libxml/parser.h>
#endif
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#if HAVE_BZLIB_H
# include <bzlib.h>
#endif
extern int init_remote_listener(int port, gboolean encrypted);
extern gboolean stand_alone;
gboolean cib_shutdown_flag = FALSE;
int cib_status = pcmk_ok;
crm_cluster_t crm_cluster;
#if SUPPORT_HEARTBEAT
oc_ev_t *cib_ev_token;
ll_cluster_t *hb_conn = NULL;
extern void oc_ev_special(const oc_ev_t *, oc_ev_class_t, int);
gboolean cib_register_ha(ll_cluster_t * hb_cluster, const char *client_name);
#else
void *hb_conn = NULL;
#endif
extern void terminate_cib(const char *caller, gboolean fast);
GMainLoop *mainloop = NULL;
const char *cib_root = NULL;
char *cib_our_uname = NULL;
gboolean preserve_status = FALSE;
gboolean cib_writes_enabled = TRUE;
int remote_fd = 0;
int remote_tls_fd = 0;
void usage(const char *cmd, int exit_status);
int cib_init(void);
void cib_shutdown(int nsig);
gboolean startCib(const char *filename);
extern int write_cib_contents(gpointer p);
GHashTable *client_list = NULL;
GHashTable *config_hash = NULL;
GHashTable *local_notify_queue = NULL;
char *channel1 = NULL;
char *channel2 = NULL;
char *channel3 = NULL;
char *channel4 = NULL;
char *channel5 = NULL;
#define OPTARGS "maswr:V?"
void cib_cleanup(void);
static void
cib_enable_writes(int nsig)
{
crm_info("(Re)enabling disk writes");
cib_writes_enabled = TRUE;
}
static void
log_cib_client(gpointer key, gpointer value, gpointer user_data)
{
cib_client_t *a_client = value;
crm_info("Client %s", crm_str(a_client->name));
}
int
main(int argc, char **argv)
{
int flag;
int rc = 0;
int argerr = 0;
#ifdef HAVE_GETOPT_H
int option_index = 0;
/* *INDENT-OFF* */
static struct option long_options[] = {
{"per-action-cib", 0, 0, 'a'},
{"stand-alone", 0, 0, 's'},
{"disk-writes", 0, 0, 'w'},
{"cib-root", 1, 0, 'r'},
{"verbose", 0, 0, 'V'},
{"help", 0, 0, '?'},
{"metadata", 0, 0, 'm'},
{0, 0, 0, 0}
};
/* *INDENT-ON* */
#endif
struct passwd *pwentry = NULL;
crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
mainloop_add_signal(SIGTERM, cib_shutdown);
mainloop_add_signal(SIGPIPE, cib_enable_writes);
cib_writer = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, NULL);
crm_peer_init();
client_list = g_hash_table_new(crm_str_hash, g_str_equal);
while (1) {
#ifdef HAVE_GETOPT_H
flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index);
#else
flag = getopt(argc, argv, OPTARGS);
#endif
if (flag == -1)
break;
switch (flag) {
case 'V':
crm_bump_log_level(argc, argv);
break;
case 's':
stand_alone = TRUE;
preserve_status = TRUE;
cib_writes_enabled = FALSE;
pwentry = getpwnam(CRM_DAEMON_USER);
CRM_CHECK(pwentry != NULL,
crm_perror(LOG_ERR, "Invalid uid (%s) specified", CRM_DAEMON_USER);
return 100);
rc = setgid(pwentry->pw_gid);
if (rc < 0) {
crm_perror(LOG_ERR, "Could not set group to %d", pwentry->pw_gid);
return 100;
}
rc = setuid(pwentry->pw_uid);
if (rc < 0) {
crm_perror(LOG_ERR, "Could not set user to %d", pwentry->pw_uid);
return 100;
}
break;
case '?': /* Help message */
usage(crm_system_name, EX_OK);
break;
case 'w':
cib_writes_enabled = TRUE;
break;
case 'r':
cib_root = optarg;
break;
case 'm':
cib_metadata();
return 0;
default:
++argerr;
break;
}
}
if (argc - optind == 1 && safe_str_eq("metadata", argv[optind])) {
cib_metadata();
return 0;
}
if (optind > argc) {
++argerr;
}
if (argerr) {
usage(crm_system_name, EX_USAGE);
}
if(cib_root == NULL) {
char *path = g_strdup_printf("%s/cib.xml", CRM_CONFIG_DIR);
char *legacy = g_strdup_printf("%s/cib.xml", CRM_LEGACY_CONFIG_DIR);
if(g_file_test(path, G_FILE_TEST_EXISTS)) {
cib_root = CRM_CONFIG_DIR;
} else if(g_file_test(legacy, G_FILE_TEST_EXISTS)) {
cib_root = CRM_LEGACY_CONFIG_DIR;
crm_notice("Using legacy config location: %s", cib_root);
} else {
cib_root = CRM_CONFIG_DIR;
crm_notice("Using new config location: %s", cib_root);
}
g_free(legacy);
g_free(path);
} else {
crm_notice("Using custom config location: %s", cib_root);
}
if (crm_is_writable(cib_root, NULL, CRM_DAEMON_USER, CRM_DAEMON_GROUP, FALSE) == FALSE) {
crm_err("Bad permissions on %s. Terminating", cib_root);
fprintf(stderr, "ERROR: Bad permissions on %s. See logs for details\n", cib_root);
fflush(stderr);
return 100;
}
/* read local config file */
rc = cib_init();
CRM_CHECK(g_hash_table_size(client_list) == 0, crm_warn("Not all clients gone at exit"));
g_hash_table_foreach(client_list, log_cib_client, NULL);
cib_cleanup();
#if SUPPORT_HEARTBEAT
if (hb_conn) {
hb_conn->llc_ops->delete(hb_conn);
}
#endif
crm_info("Done");
return rc;
}
void
cib_cleanup(void)
{
crm_peer_destroy();
if (local_notify_queue) {
g_hash_table_destroy(local_notify_queue);
}
g_hash_table_destroy(config_hash);
g_hash_table_destroy(client_list);
free(cib_our_uname);
free(channel1);
free(channel2);
free(channel3);
free(channel4);
free(channel5);
}
unsigned long cib_num_ops = 0;
const char *cib_stat_interval = "10min";
unsigned long cib_num_local = 0, cib_num_updates = 0, cib_num_fail = 0;
unsigned long cib_bad_connects = 0, cib_num_timeouts = 0;
#if SUPPORT_HEARTBEAT
gboolean ccm_connect(void);
static void
ccm_connection_destroy(gpointer user_data)
{
crm_err("CCM connection failed... blocking while we reconnect");
CRM_ASSERT(ccm_connect());
return;
}
static void *ccm_library = NULL;
gboolean
ccm_connect(void)
{
gboolean did_fail = TRUE;
int num_ccm_fails = 0;
int max_ccm_fails = 30;
int ret;
int cib_ev_fd;
int (*ccm_api_register) (oc_ev_t ** token) =
find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_register", 1);
int (*ccm_api_set_callback) (const oc_ev_t * token,
oc_ev_class_t class,
oc_ev_callback_t * fn,
oc_ev_callback_t ** prev_fn) =
find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_set_callback", 1);
void (*ccm_api_special) (const oc_ev_t *, oc_ev_class_t, int) =
find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_special", 1);
int (*ccm_api_activate) (const oc_ev_t * token, int *fd) =
find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_activate", 1);
int (*ccm_api_unregister) (oc_ev_t * token) =
find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_unregister", 1);
static struct mainloop_fd_callbacks ccm_fd_callbacks =
{
.dispatch = cib_ccm_dispatch,
.destroy = ccm_connection_destroy,
};
while (did_fail) {
did_fail = FALSE;
crm_info("Registering with CCM...");
ret = (*ccm_api_register) (&cib_ev_token);
if (ret != 0) {
did_fail = TRUE;
}
if (did_fail == FALSE) {
crm_trace("Setting up CCM callbacks");
ret = (*ccm_api_set_callback) (cib_ev_token, OC_EV_MEMB_CLASS,
cib_ccm_msg_callback, NULL);
if (ret != 0) {
crm_warn("CCM callback not set");
did_fail = TRUE;
}
}
if (did_fail == FALSE) {
(*ccm_api_special) (cib_ev_token, OC_EV_MEMB_CLASS, 0);
crm_trace("Activating CCM token");
ret = (*ccm_api_activate) (cib_ev_token, &cib_ev_fd);
if (ret != 0) {
crm_warn("CCM Activation failed");
did_fail = TRUE;
}
}
if (did_fail) {
num_ccm_fails++;
(*ccm_api_unregister) (cib_ev_token);
if (num_ccm_fails < max_ccm_fails) {
crm_warn("CCM Connection failed %d times (%d max)", num_ccm_fails, max_ccm_fails);
sleep(3);
} else {
crm_err("CCM Activation failed %d (max) times", num_ccm_fails);
return FALSE;
}
}
}
crm_debug("CCM Activation passed... all set to go!");
mainloop_add_fd("heartbeat-ccm", G_PRIORITY_MEDIUM, cib_ev_fd, cib_ev_token, &ccm_fd_callbacks);
return TRUE;
}
#endif
#if SUPPORT_COROSYNC
static gboolean
cib_ais_dispatch(int kind, const char *from, const char *data)
{
xmlNode *xml = NULL;
if (kind == crm_class_cluster) {
xml = string2xml(data);
if (xml == NULL) {
goto bail;
}
crm_xml_add(xml, F_ORIG, from);
/* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
cib_peer_callback(xml, NULL);
}
free_xml(xml);
return TRUE;
bail:
crm_err("Invalid XML: '%.120s'", data);
return TRUE;
}
static void
cib_ais_destroy(gpointer user_data)
{
if (cib_shutdown_flag) {
crm_info("Corosync disconnection complete");
} else {
crm_err("Corosync connection lost! Exiting.");
terminate_cib(__FUNCTION__, TRUE);
}
}
#endif
static void
cib_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
{
#if 0
/* crm_active_peers(crm_proc_cib) appears to give the wrong answer
* sometimes, this might help figure out why
*/
if(type == crm_status_nstate) {
crm_info("status: %s is now %s (was %s)", node->uname, node->state, (const char *)data);
if (safe_str_eq(CRMD_JOINSTATE_MEMBER, node->state)) {
return;
}
} else if(type == crm_status_processes) {
uint32_t old = 0;
if (data) {
old = *(const uint32_t *)data;
}
if ((node->processes ^ old) & crm_proc_cib) {
crm_info("status: cib process on %s is now %sactive",
node->uname, is_set(node->processes, crm_proc_cib)?"":"in");
} else {
return;
}
} else {
return;
}
#endif
if(cib_shutdown_flag && crm_active_peers() < 2 && g_hash_table_size(client_list) == 0) {
crm_info("No more peers");
terminate_cib(__FUNCTION__, FALSE);
}
}
#if SUPPORT_HEARTBEAT
static void
cib_ha_connection_destroy(gpointer user_data)
{
if (cib_shutdown_flag) {
crm_info("Heartbeat disconnection complete... exiting");
terminate_cib(__FUNCTION__, FALSE);
} else {
crm_err("Heartbeat connection lost! Exiting.");
terminate_cib(__FUNCTION__, TRUE);
}
}
#endif
int
cib_init(void)
{
if (is_openais_cluster()) {
#if SUPPORT_COROSYNC
crm_cluster.destroy = cib_ais_destroy;
crm_cluster.cs_dispatch = cib_ais_dispatch;
#endif
} else if(is_heartbeat_cluster()) {
#if SUPPORT_HEARTBEAT
crm_cluster.hb_dispatch = cib_ha_peer_callback;
crm_cluster.destroy = cib_ha_connection_destroy;
#endif
}
config_hash =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
if (startCib("cib.xml") == FALSE) {
crm_crit("Cannot start CIB... terminating");
crm_exit(1);
}
if (stand_alone == FALSE) {
if (crm_cluster_connect(&crm_cluster) == FALSE) {
crm_crit("Cannot sign in to the cluster... terminating");
crm_exit(100);
}
cib_our_uname = crm_cluster.uname;
if (is_openais_cluster()) {
crm_set_status_callback(&cib_peer_update_callback);
}
#if SUPPORT_HEARTBEAT
if (is_heartbeat_cluster()) {
gboolean was_error = FALSE;
hb_conn = crm_cluster.hb_conn;
if (was_error == FALSE) {
if (HA_OK !=
hb_conn->llc_ops->set_cstatus_callback(hb_conn, cib_client_status_callback,
hb_conn)) {
crm_err("Cannot set cstatus callback: %s", hb_conn->llc_ops->errmsg(hb_conn));
was_error = TRUE;
}
}
if (was_error == FALSE) {
was_error = (ccm_connect() == FALSE);
}
if (was_error == FALSE) {
/* Async get client status information in the cluster */
crm_info("Requesting the list of configured nodes");
hb_conn->llc_ops->client_status(hb_conn, NULL, CRM_SYSTEM_CIB, -1);
}
}
#endif
} else {
cib_our_uname = strdup("localhost");
}
ipcs_ro = mainloop_add_ipc_server(cib_channel_ro, QB_IPC_NATIVE, &ipc_ro_callbacks);
ipcs_rw = mainloop_add_ipc_server(cib_channel_rw, QB_IPC_NATIVE, &ipc_rw_callbacks);
ipcs_shm = mainloop_add_ipc_server(cib_channel_shm, QB_IPC_SHM, &ipc_rw_callbacks);
if (stand_alone) {
cib_is_master = TRUE;
}
if (ipcs_ro != NULL && ipcs_rw != NULL && ipcs_shm != NULL) {
/* Create the mainloop and run it... */
mainloop = g_main_new(FALSE);
crm_info("Starting %s mainloop", crm_system_name);
g_main_run(mainloop);
} else {
- crm_err("Couldnt start all IPC channels, exiting.");
- return -1;
+ crm_err("Failed to create IPC servers: shutting down and inhibiting respawn");
+ crm_exit(100);
}
qb_ipcs_destroy(ipcs_ro);
qb_ipcs_destroy(ipcs_rw);
qb_ipcs_destroy(ipcs_shm);
return crm_exit(0);
}
void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-%s]\n", cmd, OPTARGS);
fprintf(stream, "\t--%s (-%c)\t\tTurn on debug info."
" Additional instances increase verbosity\n", "verbose", 'V');
fprintf(stream, "\t--%s (-%c)\t\tThis help message\n", "help", '?');
fprintf(stream, "\t--%s (-%c)\t\tShow configurable cib options\n", "metadata", 'm');
fprintf(stream, "\t--%s (-%c)\tAdvanced use only\n", "per-action-cib", 'a');
fprintf(stream, "\t--%s (-%c)\tAdvanced use only\n", "stand-alone", 's');
fprintf(stream, "\t--%s (-%c)\tAdvanced use only\n", "disk-writes", 'w');
fprintf(stream, "\t--%s (-%c)\t\tAdvanced use only\n", "cib-root", 'r');
fflush(stream);
crm_exit(exit_status);
}
gboolean
startCib(const char *filename)
{
gboolean active = FALSE;
xmlNode *cib = readCibXmlFile(cib_root, filename, !preserve_status);
CRM_ASSERT(cib != NULL);
if (activateCibXml(cib, TRUE, "start") == 0) {
int port = 0;
const char *port_s = NULL;
active = TRUE;
cib_read_config(config_hash, cib);
port_s = crm_element_value(cib, "remote-tls-port");
if (port_s) {
port = crm_parse_int(port_s, "0");
remote_tls_fd = init_remote_listener(port, TRUE);
}
port_s = crm_element_value(cib, "remote-clear-port");
if (port_s) {
port = crm_parse_int(port_s, "0");
remote_fd = init_remote_listener(port, FALSE);
}
crm_info("CIB Initialization completed successfully");
}
return active;
}
diff --git a/crmd/control.c b/crmd/control.c
index 426cda7905..294a1fbd57 100644
--- a/crmd/control.c
+++ b/crmd/control.c
@@ -1,902 +1,902 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/pengine/rules.h>
#include <crm/cluster/internal.h>
#include <crmd.h>
#include <crmd_fsa.h>
#include <fsa_proto.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crmd_lrm.h>
#include <tengine.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <grp.h>
qb_ipcs_service_t *ipcs = NULL;
extern gboolean crm_connect_corosync(crm_cluster_t *cluster);
extern void crmd_ha_connection_destroy(gpointer user_data);
void crm_shutdown(int nsig);
gboolean crm_read_options(gpointer user_data);
gboolean fsa_has_quorum = FALSE;
GHashTable *ipc_clients = NULL;
crm_trigger_t *fsa_source = NULL;
crm_trigger_t *config_read = NULL;
/* A_HA_CONNECT */
void
do_ha_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
gboolean registered = FALSE;
static crm_cluster_t *cluster = NULL;
if(cluster == NULL) {
cluster = calloc(1, sizeof(crm_cluster_t));
}
if (action & A_HA_DISCONNECT) {
crm_cluster_disconnect(cluster);
crm_info("Disconnected from the cluster");
#if SUPPORT_HEARTBEAT
set_bit(fsa_input_register, R_HA_DISCONNECTED);
#endif
}
if (action & A_HA_CONNECT) {
crm_set_status_callback(&peer_update_callback);
if (is_openais_cluster()) {
#if SUPPORT_COROSYNC
registered = crm_connect_corosync(cluster);
#endif
} else if (is_heartbeat_cluster()) {
#if SUPPORT_HEARTBEAT
cluster->destroy = crmd_ha_connection_destroy;
cluster->hb_dispatch = crmd_ha_msg_callback;
registered = crm_cluster_connect(cluster);
fsa_cluster_conn = cluster->hb_conn;
crm_trace("Be informed of Node Status changes");
if (registered &&
fsa_cluster_conn->llc_ops->set_nstatus_callback(fsa_cluster_conn,
crmd_ha_status_callback,
fsa_cluster_conn) != HA_OK) {
crm_err("Cannot set nstatus callback: %s",
fsa_cluster_conn->llc_ops->errmsg(fsa_cluster_conn));
registered = FALSE;
}
crm_trace("Be informed of CRM Client Status changes");
if (registered &&
fsa_cluster_conn->llc_ops->set_cstatus_callback(fsa_cluster_conn,
crmd_client_status_callback,
fsa_cluster_conn) != HA_OK) {
crm_err("Cannot set cstatus callback: %s",
fsa_cluster_conn->llc_ops->errmsg(fsa_cluster_conn));
registered = FALSE;
}
if (registered) {
crm_trace("Requesting an initial dump of CRMD client_status");
fsa_cluster_conn->llc_ops->client_status(fsa_cluster_conn, NULL, CRM_SYSTEM_CRMD,
-1);
}
#endif
}
fsa_our_uname = cluster->uname;
fsa_our_uuid = cluster->uuid;
if (registered == FALSE) {
set_bit(fsa_input_register, R_HA_DISCONNECTED);
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
populate_cib_nodes(node_update_none, __FUNCTION__);
clear_bit(fsa_input_register, R_HA_DISCONNECTED);
crm_info("Connected to the cluster");
}
if (action & ~(A_HA_CONNECT | A_HA_DISCONNECT)) {
crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__);
}
}
/* A_SHUTDOWN */
void
do_shutdown(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
/* just in case */
set_bit(fsa_input_register, R_SHUTDOWN);
if (is_heartbeat_cluster()) {
if (is_set(fsa_input_register, pe_subsystem->flag_connected)) {
crm_info("Terminating the %s", pe_subsystem->name);
if (stop_subsystem(pe_subsystem, TRUE) == FALSE) {
/* its gone... */
crm_err("Faking %s exit", pe_subsystem->name);
clear_bit(fsa_input_register, pe_subsystem->flag_connected);
} else {
crm_info("Waiting for subsystems to exit");
crmd_fsa_stall(NULL);
}
}
crm_info("All subsystems stopped, continuing");
}
if (stonith_api) {
/* Prevent it from comming up again */
clear_bit(fsa_input_register, R_ST_REQUIRED);
crm_info("Disconnecting STONITH...");
stonith_api->cmds->disconnect(stonith_api);
}
}
/* A_SHUTDOWN_REQ */
void
do_shutdown_req(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
xmlNode *msg = NULL;
crm_info("Sending shutdown request to %s", crm_str(fsa_our_dc));
msg = create_request(CRM_OP_SHUTDOWN_REQ, NULL, NULL, CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
/* set_bit(fsa_input_register, R_STAYDOWN); */
if (send_cluster_message(NULL, crm_msg_crmd, msg, TRUE) == FALSE) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
free_xml(msg);
}
extern crm_ipc_t *attrd_ipc;
extern char *max_generation_from;
extern xmlNode *max_generation_xml;
extern GHashTable *resource_history;
extern GHashTable *voted;
extern GHashTable *reload_hash;
void log_connected_client(gpointer key, gpointer value, gpointer user_data);
void
log_connected_client(gpointer key, gpointer value, gpointer user_data)
{
crmd_client_t *client = value;
crm_err("%s is still connected at exit", client->table_key);
}
int
crmd_exit(int rc)
{
GListPtr gIter = NULL;
if(attrd_ipc) {
crm_ipc_close(attrd_ipc);
crm_ipc_destroy(attrd_ipc);
}
if(crmd_mainloop) {
g_main_loop_quit(crmd_mainloop);
g_main_loop_unref(crmd_mainloop);
}
#if SUPPORT_HEARTBEAT
if (fsa_cluster_conn) {
fsa_cluster_conn->llc_ops->delete(fsa_cluster_conn);
fsa_cluster_conn = NULL;
}
#endif
for(gIter = fsa_message_queue; gIter != NULL; gIter = gIter->next) {
fsa_data_t *fsa_data = gIter->data;
crm_info("Dropping %s: [ state=%s cause=%s origin=%s ]",
fsa_input2string(fsa_data->fsa_input),
fsa_state2string(fsa_state),
fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin);
delete_fsa_input(fsa_data);
}
g_list_free(fsa_message_queue);
if (ipc_clients) {
crm_debug("Number of connected clients: %d", g_hash_table_size(ipc_clients));
/* g_hash_table_foreach(ipc_clients, log_connected_client, NULL); */
g_hash_table_destroy(ipc_clients);
}
empty_uuid_cache();
crm_peer_destroy();
clear_bit(fsa_input_register, R_MEMBERSHIP);
if (te_subsystem->client && te_subsystem->client->ipc) {
crm_debug("Full destroy: TE");
qb_ipcs_disconnect(te_subsystem->client->ipc);
}
free(te_subsystem);
if (pe_subsystem->client && pe_subsystem->client->ipc) {
crm_debug("Full destroy: PE");
qb_ipcs_disconnect(pe_subsystem->client->ipc);
}
free(pe_subsystem);
free(cib_subsystem);
if (integrated_nodes) {
g_hash_table_destroy(integrated_nodes);
}
if (finalized_nodes) {
g_hash_table_destroy(finalized_nodes);
}
if (confirmed_nodes) {
g_hash_table_destroy(confirmed_nodes);
}
if (reload_hash) {
g_hash_table_destroy(reload_hash);
}
if (resource_history) {
g_hash_table_destroy(resource_history);
}
if (voted) {
g_hash_table_destroy(voted);
}
cib_delete(fsa_cib_conn);
fsa_cib_conn = NULL;
if (fsa_lrm_conn) {
lrmd_api_delete(fsa_lrm_conn);
fsa_lrm_conn = NULL;
}
free(transition_timer);
free(integration_timer);
free(finalization_timer);
free(election_trigger);
free(election_timeout);
free(shutdown_escalation_timer);
free(wait_timer);
free(recheck_timer);
free(fsa_our_dc_version);
free(fsa_our_uname);
free(fsa_our_uuid);
free(fsa_our_dc);
free(max_generation_from);
free_xml(max_generation_xml);
return crm_exit(rc);
}
/* A_EXIT_0, A_EXIT_1 */
void
do_exit(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
int exit_code = 0;
int log_level = LOG_INFO;
const char *exit_type = "gracefully";
if (action & A_EXIT_1) {
exit_code = 1;
log_level = LOG_ERR;
exit_type = "forcefully";
}
verify_stopped(cur_state, LOG_ERR);
do_crm_log(log_level, "Performing %s - %s exiting the CRMd",
fsa_action2string(action), exit_type);
if (is_set(fsa_input_register, R_IN_RECOVERY)) {
crm_err("Could not recover from internal error");
exit_code = 2;
}
if (is_set(fsa_input_register, R_STAYDOWN)) {
crm_warn("Inhibiting respawn by Heartbeat");
exit_code = 100;
}
crm_info("[%s] stopped (%d)", crm_system_name, exit_code);
delete_fsa_input(msg_data);
crmd_exit(exit_code);
}
/* A_STARTUP */
void
do_startup(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
int was_error = 0;
int interval = 1; /* seconds between DC heartbeats */
crm_debug("Registering Signal Handlers");
mainloop_add_signal(SIGTERM, crm_shutdown);
fsa_source = mainloop_add_trigger(G_PRIORITY_HIGH, crm_fsa_trigger, NULL);
config_read = mainloop_add_trigger(G_PRIORITY_HIGH, crm_read_options, NULL);
ipc_clients = g_hash_table_new(crm_str_hash, g_str_equal);
crm_debug("Creating CIB and LRM objects");
fsa_cib_conn = cib_new();
fsa_lrm_conn = lrmd_api_new();
/* set up the timers */
transition_timer = calloc(1, sizeof(fsa_timer_t));
integration_timer = calloc(1, sizeof(fsa_timer_t));
finalization_timer = calloc(1, sizeof(fsa_timer_t));
election_trigger = calloc(1, sizeof(fsa_timer_t));
election_timeout = calloc(1, sizeof(fsa_timer_t));
shutdown_escalation_timer = calloc(1, sizeof(fsa_timer_t));
wait_timer = calloc(1, sizeof(fsa_timer_t));
recheck_timer = calloc(1, sizeof(fsa_timer_t));
interval = interval * 1000;
if (election_trigger != NULL) {
election_trigger->source_id = 0;
election_trigger->period_ms = -1;
election_trigger->fsa_input = I_DC_TIMEOUT;
election_trigger->callback = crm_timer_popped;
election_trigger->repeat = FALSE;
} else {
was_error = TRUE;
}
if (election_timeout != NULL) {
election_timeout->source_id = 0;
election_timeout->period_ms = -1;
election_timeout->fsa_input = I_ELECTION_DC;
election_timeout->callback = crm_timer_popped;
election_timeout->repeat = FALSE;
} else {
was_error = TRUE;
}
if (transition_timer != NULL) {
transition_timer->source_id = 0;
transition_timer->period_ms = -1;
transition_timer->fsa_input = I_PE_CALC;
transition_timer->callback = crm_timer_popped;
transition_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (integration_timer != NULL) {
integration_timer->source_id = 0;
integration_timer->period_ms = -1;
integration_timer->fsa_input = I_INTEGRATED;
integration_timer->callback = crm_timer_popped;
integration_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (finalization_timer != NULL) {
finalization_timer->source_id = 0;
finalization_timer->period_ms = -1;
finalization_timer->fsa_input = I_FINALIZED;
finalization_timer->callback = crm_timer_popped;
finalization_timer->repeat = FALSE;
/* for possible enabling... a bug in the join protocol left
* a slave in S_PENDING while we think its in S_NOT_DC
*
* raising I_FINALIZED put us into a transition loop which is
* never resolved.
* in this loop we continually send probes which the node
* NACK's because its in S_PENDING
*
* if we have nodes where heartbeat is active but the
* CRM is not... then this will be handled in the
* integration phase
*/
finalization_timer->fsa_input = I_ELECTION;
} else {
was_error = TRUE;
}
if (shutdown_escalation_timer != NULL) {
shutdown_escalation_timer->source_id = 0;
shutdown_escalation_timer->period_ms = -1;
shutdown_escalation_timer->fsa_input = I_STOP;
shutdown_escalation_timer->callback = crm_timer_popped;
shutdown_escalation_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (wait_timer != NULL) {
wait_timer->source_id = 0;
wait_timer->period_ms = 2000;
wait_timer->fsa_input = I_NULL;
wait_timer->callback = crm_timer_popped;
wait_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (recheck_timer != NULL) {
recheck_timer->source_id = 0;
recheck_timer->period_ms = -1;
recheck_timer->fsa_input = I_PE_CALC;
recheck_timer->callback = crm_timer_popped;
recheck_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
/* set up the sub systems */
cib_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
te_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
pe_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
if (cib_subsystem != NULL) {
cib_subsystem->pid = -1;
cib_subsystem->name = CRM_SYSTEM_CIB;
cib_subsystem->flag_connected = R_CIB_CONNECTED;
cib_subsystem->flag_required = R_CIB_REQUIRED;
} else {
was_error = TRUE;
}
if (te_subsystem != NULL) {
te_subsystem->pid = -1;
te_subsystem->name = CRM_SYSTEM_TENGINE;
te_subsystem->flag_connected = R_TE_CONNECTED;
te_subsystem->flag_required = R_TE_REQUIRED;
} else {
was_error = TRUE;
}
if (pe_subsystem != NULL) {
pe_subsystem->pid = -1;
pe_subsystem->path = CRM_DAEMON_DIR;
pe_subsystem->name = CRM_SYSTEM_PENGINE;
pe_subsystem->command = CRM_DAEMON_DIR "/" CRM_SYSTEM_PENGINE;
pe_subsystem->args = NULL;
pe_subsystem->flag_connected = R_PE_CONNECTED;
pe_subsystem->flag_required = R_PE_REQUIRED;
} else {
was_error = TRUE;
}
if (was_error == FALSE && is_heartbeat_cluster()) {
if(start_subsystem(pe_subsystem) == FALSE) {
was_error = TRUE;
}
}
if (was_error) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
welcomed_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
integrated_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
finalized_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
confirmed_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
static int32_t
crmd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
{
crmd_client_t *blank_client = NULL;
#if ENABLE_ACL
struct group *crm_grp = NULL;
#endif
crm_trace("Connecting %p for uid=%d gid=%d", c, uid, gid);
blank_client = calloc(1, sizeof(crmd_client_t));
CRM_ASSERT(blank_client != NULL);
crm_trace("Created client: %p", blank_client);
blank_client->ipc = c;
blank_client->sub_sys = NULL;
blank_client->uuid = NULL;
blank_client->table_key = NULL;
#if ENABLE_ACL
crm_grp = getgrnam(CRM_DAEMON_GROUP);
if (crm_grp) {
qb_ipcs_connection_auth_set(c, -1, crm_grp->gr_gid, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
}
blank_client->user = uid2username(uid);
#endif
qb_ipcs_context_set(c, blank_client);
return 0;
}
static void
crmd_ipc_created(qb_ipcs_connection_t *c)
{
crm_trace("Client %p connected", c);
}
static int32_t
crmd_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
{
uint32_t id = 0;
uint32_t flags = 0;
crmd_client_t *client = qb_ipcs_context_get(c);
xmlNode *msg = crm_ipcs_recv(c, data, size, &id, &flags);
crm_trace("Invoked: %s", client->table_key);
if(flags & crm_ipc_client_response) {
crm_ipcs_send_ack(c, id, "ack", __FUNCTION__, __LINE__);
}
if (msg == NULL) {
return 0;
}
#if ENABLE_ACL
determine_request_user(client->user, msg, F_CRM_USER);
#endif
crm_trace("Processing msg from %s", client->table_key);
crm_log_xml_trace(msg, "CRMd[inbound]");
if (crmd_authorize_message(msg, client)) {
route_message(C_IPC_MESSAGE, msg);
}
trigger_fsa(fsa_source);
free_xml(msg);
return 0;
}
static int32_t
crmd_ipc_closed(qb_ipcs_connection_t *c)
{
return 0;
}
static void
crmd_ipc_destroy(qb_ipcs_connection_t *c)
{
crmd_client_t *client = qb_ipcs_context_get(c);
if (client == NULL) {
crm_trace("No client to delete");
return;
}
process_client_disconnect(client);
crm_trace("Disconnecting client %s (%p)", client->table_key, client);
free(client->table_key);
free(client->sub_sys);
free(client->uuid);
free(client->user);
free(client);
trigger_fsa(fsa_source);
}
/* A_STOP */
void
do_stop(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
if (is_heartbeat_cluster()) {
stop_subsystem(pe_subsystem, FALSE);
}
mainloop_del_ipc_server(ipcs);
register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
}
/* A_STARTED */
void
do_started(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
static struct qb_ipcs_service_handlers crmd_callbacks =
{
.connection_accept = crmd_ipc_accept,
.connection_created = crmd_ipc_created,
.msg_process = crmd_ipc_dispatch,
.connection_closed = crmd_ipc_closed,
.connection_destroyed = crmd_ipc_destroy
};
if (cur_state != S_STARTING) {
crm_err("Start cancelled... %s", fsa_state2string(cur_state));
return;
} else if (is_set(fsa_input_register, R_MEMBERSHIP) == FALSE) {
crm_info("Delaying start, no membership data (%.16llx)", R_MEMBERSHIP);
crmd_fsa_stall(NULL);
return;
} else if (is_set(fsa_input_register, R_LRM_CONNECTED) == FALSE) {
crm_info("Delaying start, LRM not connected (%.16llx)", R_LRM_CONNECTED);
crmd_fsa_stall(NULL);
return;
} else if (is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) {
crm_info("Delaying start, CIB not connected (%.16llx)", R_CIB_CONNECTED);
crmd_fsa_stall(NULL);
return;
} else if (is_set(fsa_input_register, R_READ_CONFIG) == FALSE) {
crm_info("Delaying start, Config not read (%.16llx)", R_READ_CONFIG);
crmd_fsa_stall(NULL);
return;
} else if (is_set(fsa_input_register, R_PEER_DATA) == FALSE) {
/* try reading from HA */
crm_info("Delaying start, No peer data (%.16llx)", R_PEER_DATA);
#if SUPPORT_HEARTBEAT
if (is_heartbeat_cluster()) {
HA_Message *msg = NULL;
crm_trace("Looking for a HA message");
msg = fsa_cluster_conn->llc_ops->readmsg(fsa_cluster_conn, 0);
if (msg != NULL) {
crm_trace("There was a HA message");
ha_msg_del(msg);
}
}
#endif
crmd_fsa_stall(NULL);
return;
}
crm_debug("Init server comms");
ipcs = mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, &crmd_callbacks);
if (ipcs == NULL) {
- crm_err("Couldn't start IPC server");
+ crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
if (stonith_reconnect == NULL) {
int dummy;
stonith_reconnect = mainloop_add_trigger(G_PRIORITY_LOW, te_connect_stonith, &dummy);
}
set_bit(fsa_input_register, R_ST_REQUIRED);
mainloop_set_trigger(stonith_reconnect);
crm_notice("The local CRM is operational");
clear_bit(fsa_input_register, R_STARTING);
register_fsa_input(msg_data->fsa_cause, I_PENDING, NULL);
}
/* A_RECOVER */
void
do_recover(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
set_bit(fsa_input_register, R_IN_RECOVERY);
crm_err("Action %s (%.16llx) not supported", fsa_action2string(action), action);
register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
}
/* *INDENT-OFF* */
pe_cluster_option crmd_opts[] = {
/* name, old-name, validate, default, description */
{ "dc-version", NULL, "string", NULL, "none", NULL, "Version of Pacemaker on the cluster's DC.", "Includes the hash which identifies the exact Mercurial changeset it was built from. Used for diagnostic purposes." },
{ "cluster-infrastructure", NULL, "string", NULL, "heartbeat", NULL, "The messaging stack on which Pacemaker is currently running.", "Used for informational and diagnostic purposes." },
{ XML_CONFIG_ATTR_DC_DEADTIME, "dc_deadtime", "time", NULL, "20s", &check_time, "How long to wait for a response from other nodes during startup.", "The \"correct\" value will depend on the speed/load of your network and the type of switches used." },
{ XML_CONFIG_ATTR_RECHECK, "cluster_recheck_interval", "time",
"Zero disables polling. Positive values are an interval in seconds (unless other SI units are specified. eg. 5min)", "15min", &check_timer,
"Polling interval for time based changes to options, resource parameters and constraints.",
"The Cluster is primarily event driven, however the configuration can have elements that change based on time."
" To ensure these changes take effect, we can optionally poll the cluster's status for changes." },
{ XML_CONFIG_ATTR_ELECTION_FAIL, "election_timeout", "time", NULL, "2min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
{ XML_CONFIG_ATTR_FORCE_QUIT, "shutdown_escalation", "time", NULL, "20min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
{ "crmd-integration-timeout", NULL, "time", NULL, "3min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
{ "crmd-finalization-timeout", NULL, "time", NULL, "30min", &check_timer, "*** Advanced Use Only ***.", "If you need to adjust this value, it probably indicates the presence of a bug." },
{ "crmd-transition-delay", NULL, "time", NULL, "0s", &check_timer, "*** Advanced Use Only ***\nEnabling this option will slow down cluster recovery under all conditions", "Delay cluster recovery for the configured interval to allow for additional/related events to occur.\nUseful if your configuration is sensitive to the order in which ping updates arrive." },
{ XML_ATTR_EXPECTED_VOTES, NULL, "integer", NULL, "2", &check_number, "The number of nodes expected to be in the cluster", "Used to calculate quorum in openais based clusters." },
};
/* *INDENT-ON* */
void
crmd_metadata(void)
{
config_metadata("CRM Daemon", "1.0",
"CRM Daemon Options",
"This is a fake resource that details the options that can be configured for the CRM Daemon.",
crmd_opts, DIMOF(crmd_opts));
}
static void
verify_crmd_options(GHashTable * options)
{
verify_all_options(options, crmd_opts, DIMOF(crmd_opts));
}
static const char *
crmd_pref(GHashTable * options, const char *name)
{
return get_cluster_pref(options, crmd_opts, DIMOF(crmd_opts), name);
}
static void
config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
const char *value = NULL;
GHashTable *config_hash = NULL;
crm_time_t *now = crm_time_new(NULL);
if (rc != pcmk_ok) {
fsa_data_t *msg_data = NULL;
crm_err("Local CIB query resulted in an error: %s", pcmk_strerror(rc));
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
if (rc == -EACCES || rc == -pcmk_err_dtd_validation) {
crm_err("The cluster is mis-configured - shutting down and staying down");
set_bit(fsa_input_register, R_STAYDOWN);
}
goto bail;
}
crm_debug("Call %d : Parsing CIB options", call_id);
config_hash =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
unpack_instance_attributes(output, output, XML_CIB_TAG_PROPSET, NULL, config_hash,
CIB_OPTIONS_FIRST, FALSE, now);
verify_crmd_options(config_hash);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_DC_DEADTIME);
election_trigger->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_FORCE_QUIT);
shutdown_escalation_timer->period_ms = crm_get_msec(value);
crm_debug("Shutdown escalation occurs after: %dms", shutdown_escalation_timer->period_ms);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_ELECTION_FAIL);
election_timeout->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_RECHECK);
recheck_timer->period_ms = crm_get_msec(value);
crm_debug("Checking for expired actions every %dms", recheck_timer->period_ms);
value = crmd_pref(config_hash, "crmd-transition-delay");
transition_timer->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, "crmd-integration-timeout");
integration_timer->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, "crmd-finalization-timeout");
finalization_timer->period_ms = crm_get_msec(value);
#if SUPPORT_COROSYNC
if (is_classic_ais_cluster()) {
value = crmd_pref(config_hash, XML_ATTR_EXPECTED_VOTES);
crm_debug("Sending expected-votes=%s to corosync", value);
send_ais_text(crm_class_quorum, value, TRUE, NULL, crm_msg_ais);
}
#endif
set_bit(fsa_input_register, R_READ_CONFIG);
crm_trace("Triggering FSA: %s", __FUNCTION__);
mainloop_set_trigger(fsa_source);
g_hash_table_destroy(config_hash);
bail:
crm_time_free(now);
}
gboolean
crm_read_options(gpointer user_data)
{
int call_id =
fsa_cib_conn->cmds->query(fsa_cib_conn, XML_CIB_TAG_CRMCONFIG, NULL, cib_scope_local);
add_cib_op_callback(fsa_cib_conn, call_id, FALSE, NULL, config_query_callback);
crm_trace("Querying the CIB... call %d", call_id);
return TRUE;
}
/* A_READCONFIG */
void
do_read_config(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
mainloop_set_trigger(config_read);
}
void
crm_shutdown(int nsig)
{
if (crmd_mainloop != NULL && g_main_is_running(crmd_mainloop)) {
if (is_set(fsa_input_register, R_SHUTDOWN)) {
crm_err("Escalating the shutdown");
register_fsa_input_before(C_SHUTDOWN, I_ERROR, NULL);
} else {
set_bit(fsa_input_register, R_SHUTDOWN);
register_fsa_input(C_SHUTDOWN, I_SHUTDOWN, NULL);
if (shutdown_escalation_timer->period_ms < 1) {
const char *value = crmd_pref(NULL, XML_CONFIG_ATTR_FORCE_QUIT);
int msec = crm_get_msec(value);
crm_debug("Using default shutdown escalation: %dms", msec);
shutdown_escalation_timer->period_ms = msec;
}
/* cant rely on this... */
crm_notice("Requesting shutdown, upper limit is %dms", shutdown_escalation_timer->period_ms);
crm_timer_start(shutdown_escalation_timer);
}
} else {
crm_info("exit from shutdown");
crmd_exit(EX_OK);
}
}
diff --git a/fencing/main.c b/fencing/main.c
index 0af0dc58f3..2b8d6cdc4a 100644
--- a/fencing/main.c
+++ b/fencing/main.c
@@ -1,1072 +1,1070 @@
/*
* Copyright (C) 2009 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/common/xml.h>
#include <crm/common/mainloop.h>
#include <crm/cib/internal.h>
#include <internal.h>
#include <standalone_config.h>
char *stonith_our_uname = NULL;
GMainLoop *mainloop = NULL;
GHashTable *client_list = NULL;
gboolean stand_alone = FALSE;
gboolean no_cib_connect = FALSE;
gboolean stonith_shutdown_flag = FALSE;
qb_ipcs_service_t *ipcs = NULL;
static void stonith_shutdown(int nsig);
static void stonith_cleanup(void);
static int32_t
st_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
{
crm_trace("Connecting %p for uid=%d gid=%d", c, uid, gid);
if(stonith_shutdown_flag) {
crm_info("Ignoring new client [%d] during shutdown", crm_ipcs_client_pid(c));
return -EPERM;
}
return 0;
}
static void
st_ipc_created(qb_ipcs_connection_t *c)
{
stonith_client_t *new_client = NULL;
#if 0
struct qb_ipcs_stats srv_stats;
qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
qb_log(LOG_INFO, "Connection created (active:%d, closed:%d)",
srv_stats.active_connections,
srv_stats.closed_connections);
#endif
new_client = calloc(1, sizeof(stonith_client_t));
new_client->channel = c;
new_client->pid = crm_ipcs_client_pid(c);
new_client->channel_name = strdup("ipc");
CRM_CHECK(new_client->id == NULL, free(new_client->id));
new_client->id = crm_generate_uuid();
crm_trace("Created channel %p for client %s", c, new_client->id);
/* make sure we can find ourselves later for sync calls
* redirected to the master instance
*/
g_hash_table_insert(client_list, new_client->id, new_client);
qb_ipcs_context_set(c, new_client);
CRM_ASSERT(qb_ipcs_context_get(c) != NULL);
}
/* Exit code means? */
static int32_t
st_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
{
uint32_t id = 0;
uint32_t flags = 0;
xmlNode *request = NULL;
stonith_client_t *client = (stonith_client_t*)qb_ipcs_context_get(c);
request = crm_ipcs_recv(c, data, size, &id, &flags);
if (request == NULL) {
crm_ipcs_send_ack(c, id, "nack", __FUNCTION__, __LINE__);
return 0;
}
CRM_CHECK(client != NULL, goto cleanup);
if(client->name == NULL) {
const char *value = crm_element_value(request, F_STONITH_CLIENTNAME);
if(value == NULL) {
value = "unknown";
}
client->name = g_strdup_printf("%s.%u", value, client->pid);
}
CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p/%s", client, client->name); goto cleanup);
if(flags & crm_ipc_client_response) {
CRM_LOG_ASSERT(client->request_id == 0); /* This means the client has two synchronous events in-flight */
client->request_id = id; /* Reply only to the last one */
}
crm_xml_add(request, F_STONITH_CLIENTID, client->id);
crm_xml_add(request, F_STONITH_CLIENTNAME, client->name);
crm_xml_add(request, F_STONITH_CLIENTNODE, stonith_our_uname);
crm_log_xml_trace(request, "Client[inbound]");
stonith_command(client, id, flags, request, NULL);
cleanup:
if(client == NULL || client->id == NULL) {
crm_log_xml_notice(request, "Invalid client");
}
free_xml(request);
return 0;
}
/* Error code means? */
static int32_t
st_ipc_closed(qb_ipcs_connection_t *c)
{
stonith_client_t *client = (stonith_client_t*)qb_ipcs_context_get(c);
#if 0
qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
qb_ipcs_connection_stats_get(c, &stats, QB_FALSE);
qb_log(LOG_INFO, "Connection to pid:%d destroyed (active:%d, closed:%d)",
stats.client_pid,
srv_stats.active_connections,
srv_stats.closed_connections);
qb_log(LOG_DEBUG, " Requests %"PRIu64"", stats.requests);
qb_log(LOG_DEBUG, " Responses %"PRIu64"", stats.responses);
qb_log(LOG_DEBUG, " Events %"PRIu64"", stats.events);
qb_log(LOG_DEBUG, " Send retries %"PRIu64"", stats.send_retries);
qb_log(LOG_DEBUG, " Recv retries %"PRIu64"", stats.recv_retries);
qb_log(LOG_DEBUG, " FC state %d", stats.flow_control_state);
qb_log(LOG_DEBUG, " FC count %"PRIu64"", stats.flow_control_count);
#endif
if (client == NULL) {
crm_err("No client");
return 0;
}
crm_trace("Cleaning up after client disconnect: %p/%s/%s", client, crm_str(client->name), client->id);
if(client->id != NULL) {
g_hash_table_remove(client_list, client->id);
}
/* 0 means: yes, go ahead and destroy the connection */
return 0;
}
static void
st_ipc_destroy(qb_ipcs_connection_t *c)
{
stonith_client_t *client = (stonith_client_t*)qb_ipcs_context_get(c);
/* Make sure the connection is fully cleaned up */
st_ipc_closed(c);
if(client == NULL) {
crm_trace("Nothing to destroy");
return;
}
crm_trace("Destroying %s (%p)", client->name, client);
free(client->name);
free(client->id);
free(client);
crm_trace("Done");
return;
}
static void
stonith_peer_callback(xmlNode * msg, void* private_data)
{
const char *remote_peer = crm_element_value(msg, F_ORIG);
const char *op = crm_element_value(msg, F_STONITH_OPERATION);
if(crm_str_eq(op, "poke", TRUE)) {
return;
}
crm_log_xml_trace(msg, "Peer[inbound]");
stonith_command(NULL, 0, 0, msg, remote_peer);
}
#if SUPPORT_HEARTBEAT
static void
stonith_peer_hb_callback(HA_Message * msg, void* private_data)
{
xmlNode *xml = convert_ha_message(NULL, msg, __FUNCTION__);
stonith_peer_callback(xml, private_data);
free_xml(xml);
}
static void
stonith_peer_hb_destroy(gpointer user_data)
{
if(stonith_shutdown_flag) {
crm_info("Heartbeat disconnection complete... exiting");
} else {
crm_err("Heartbeat connection lost! Exiting.");
}
stonith_shutdown(0);
}
#endif
#if SUPPORT_COROSYNC
static gboolean stonith_peer_ais_callback(int kind, const char *from, const char *data)
{
xmlNode *xml = NULL;
if(kind == crm_class_cluster) {
xml = string2xml(data);
if(xml == NULL) {
goto bail;
}
crm_xml_add(xml, F_ORIG, from);
/* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
stonith_peer_callback(xml, NULL);
}
free_xml(xml);
return TRUE;
bail:
crm_err("Invalid XML: '%.120s'", data);
return TRUE;
}
static void
stonith_peer_ais_destroy(gpointer user_data)
{
crm_err("AIS connection terminated");
stonith_shutdown(0);
}
#endif
void do_local_reply(xmlNode *notify_src, const char *client_id,
gboolean sync_reply, gboolean from_peer)
{
/* send callback to originating child */
stonith_client_t *client_obj = NULL;
int local_rc = pcmk_ok;
crm_trace("Sending response");
if(client_id != NULL) {
client_obj = g_hash_table_lookup(client_list, client_id);
} else {
crm_trace("No client to sent the response to."
" F_STONITH_CLIENTID not set.");
}
crm_trace("Sending callback to request originator");
if(client_obj == NULL) {
local_rc = -1;
} else {
int rid = 0;
if(sync_reply) {
CRM_LOG_ASSERT(client_obj->request_id);
rid = client_obj->request_id;
client_obj->request_id = 0;
crm_trace("Sending response %d to %s %s",
rid, client_obj->name, from_peer?"(originator of delegated request)":"");
} else {
crm_trace("Sending an event to %s %s",
client_obj->name, from_peer?"(originator of delegated request)":"");
}
local_rc = crm_ipcs_send(client_obj->channel, rid, notify_src, !sync_reply);
}
if(local_rc < pcmk_ok && client_obj != NULL) {
crm_warn("%sSync reply to %s failed: %s",
sync_reply?"":"A-",
client_obj?client_obj->name:"<unknown>", pcmk_strerror(local_rc));
}
}
long long get_stonith_flag(const char *name)
{
if(safe_str_eq(name, T_STONITH_NOTIFY_FENCE)) {
return 0x01;
} else if(safe_str_eq(name, STONITH_OP_DEVICE_ADD)) {
return 0x04;
} else if(safe_str_eq(name, STONITH_OP_DEVICE_DEL)) {
return 0x10;
}
return 0;
}
static void
stonith_notify_client(gpointer key, gpointer value, gpointer user_data)
{
xmlNode *update_msg = user_data;
stonith_client_t *client = value;
const char *type = NULL;
CRM_CHECK(client != NULL, return);
CRM_CHECK(update_msg != NULL, return);
type = crm_element_value(update_msg, F_SUBTYPE);
CRM_CHECK(type != NULL, crm_log_xml_err(update_msg, "notify"); return);
if(client->channel == NULL) {
crm_trace("Skipping client with NULL channel");
return;
} else if(client->name == NULL) {
crm_trace("Skipping unnammed client / comamnd channel");
return;
}
if(client->flags & get_stonith_flag(type)) {
int rc = crm_ipcs_send(client->channel, 0, update_msg, crm_ipc_server_event|crm_ipc_server_error);
if(rc <= 0) {
crm_warn("%s notification of client %s.%.6s failed: %s (%d)",
type, client->name, client->id, pcmk_strerror(rc), rc);
} else {
crm_trace("Sent %s notification to client %s.%.6s", type, client->name, client->id);
}
}
}
void
do_stonith_async_timeout_update(const char *client_id, const char *call_id, int timeout)
{
stonith_client_t *client = NULL;
xmlNode *notify_data = NULL;
if (!timeout || !call_id || !client_id) {
return;
}
client = g_hash_table_lookup(client_list, client_id);
if (!client) {
return;
}
notify_data = create_xml_node(NULL, T_STONITH_TIMEOUT_VALUE);
crm_xml_add(notify_data, F_TYPE, T_STONITH_TIMEOUT_VALUE);
crm_xml_add(notify_data, F_STONITH_CALLID, call_id);
crm_xml_add_int(notify_data, F_STONITH_TIMEOUT, timeout);
crm_trace("timeout update is %d for client %s and call id %s", timeout, client_id, call_id);
if (client->channel) {
crm_ipcs_send(client->channel, 0, notify_data, crm_ipc_server_event);
}
free_xml(notify_data);
}
void
do_stonith_notify(int options, const char *type, int result, xmlNode *data)
{
/* TODO: Standardize the contents of data */
xmlNode *update_msg = create_xml_node(NULL, "notify");
CRM_CHECK(type != NULL, ;);
crm_xml_add(update_msg, F_TYPE, T_STONITH_NOTIFY);
crm_xml_add(update_msg, F_SUBTYPE, type);
crm_xml_add(update_msg, F_STONITH_OPERATION, type);
crm_xml_add_int(update_msg, F_STONITH_RC, result);
if(data != NULL) {
add_message_xml(update_msg, F_STONITH_CALLDATA, data);
}
crm_trace("Notifying clients");
g_hash_table_foreach(client_list, stonith_notify_client, update_msg);
free_xml(update_msg);
crm_trace("Notify complete");
}
static stonith_key_value_t *parse_device_list(const char *devices)
{
int lpc = 0;
int max = 0;
int last = 0;
stonith_key_value_t *output = NULL;
if(devices == NULL) {
return output;
}
max = strlen(devices);
for(lpc = 0; lpc <= max; lpc++) {
if(devices[lpc] == ',' || devices[lpc] == 0) {
char *line = NULL;
line = calloc(1, 2 + lpc - last);
snprintf(line, 1 + lpc - last, "%s", devices+last);
output = stonith_key_value_add(output, NULL, line);
free(line);
last = lpc + 1;
}
}
return output;
}
static void topology_remove_helper(const char *node, int level)
{
int rc;
char *desc = NULL;
xmlNode *data = create_xml_node(NULL, F_STONITH_LEVEL);
xmlNode *notify_data = create_xml_node(NULL, STONITH_OP_LEVEL_DEL);
crm_xml_add(data, "origin", __FUNCTION__);
crm_xml_add_int(data, XML_ATTR_ID, level);
crm_xml_add(data, F_STONITH_TARGET, node);
rc = stonith_level_remove(data, &desc);
crm_xml_add(notify_data, F_STONITH_DEVICE, desc);
crm_xml_add_int(notify_data, F_STONITH_ACTIVE, g_hash_table_size(topology));
do_stonith_notify(0, STONITH_OP_LEVEL_DEL, rc, notify_data);
free_xml(notify_data);
free_xml(data);
free(desc);
}
static void topology_register_helper(const char *node, int level, stonith_key_value_t *device_list)
{
int rc;
char *desc = NULL;
xmlNode *notify_data = create_xml_node(NULL, STONITH_OP_LEVEL_ADD);
xmlNode *data = create_level_registration_xml(node, level, device_list);
rc = stonith_level_register(data, &desc);
crm_xml_add(notify_data, F_STONITH_DEVICE, desc);
crm_xml_add_int(notify_data, F_STONITH_ACTIVE, g_hash_table_size(topology));
do_stonith_notify(0, STONITH_OP_LEVEL_ADD, rc, notify_data);
free_xml(notify_data);
free_xml(data);
free(desc);
}
static void remove_cib_device(xmlXPathObjectPtr xpathObj)
{
int max = 0, lpc = 0;
if(xpathObj && xpathObj->nodesetval) {
max = xpathObj->nodesetval->nodeNr;
}
for(lpc = 0; lpc < max; lpc++) {
const char *rsc_id = NULL;
const char *standard = NULL;
xmlNode *match = getXpathResult(xpathObj, lpc);
CRM_CHECK(match != NULL, continue);
standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
if (safe_str_neq(standard, "stonith")) {
continue;
}
rsc_id = crm_element_value(match, XML_ATTR_ID);
stonith_device_remove(rsc_id, TRUE);
}
}
static void remove_fencing_topology(xmlXPathObjectPtr xpathObj)
{
int max = 0, lpc = 0;
if(xpathObj && xpathObj->nodesetval) {
max = xpathObj->nodesetval->nodeNr;
}
for(lpc = 0; lpc < max; lpc++) {
xmlNode *match = getXpathResult(xpathObj, lpc);
CRM_CHECK(match != NULL, continue);
if(crm_element_value(match, XML_DIFF_MARKER)) {
/* Deletion */
int index = 0;
const char *target = crm_element_value(match, XML_ATTR_STONITH_TARGET);
crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
if(target == NULL) {
crm_err("Invalid fencing target in element %s", ID(match));
} else if(index <= 0) {
crm_err("Invalid level for %s in element %s", target, ID(match));
} else {
topology_remove_helper(target, index);
}
/* } else { Deal with modifications during the 'addition' stage */
}
}
}
static void register_cib_device(xmlXPathObjectPtr xpathObj, gboolean force)
{
int max = 0, lpc = 0;
if(xpathObj && xpathObj->nodesetval) {
max = xpathObj->nodesetval->nodeNr;
}
for(lpc = 0; lpc < max; lpc++) {
const char *rsc_id = NULL;
const char *agent = NULL;
const char *standard = NULL;
const char *provider = NULL;
stonith_key_value_t *params = NULL;
xmlNode *match = getXpathResult(xpathObj, lpc);
xmlNode *attributes;
xmlNode *attr;
xmlNode *data;
CRM_CHECK(match != NULL, continue);
standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
agent = crm_element_value(match, XML_EXPR_ATTR_TYPE);
provider = crm_element_value(match, XML_AGENT_ATTR_PROVIDER);
if (safe_str_neq(standard, "stonith") || !agent) {
continue;
}
rsc_id = crm_element_value(match, XML_ATTR_ID);
attributes = find_xml_node(match, XML_TAG_ATTR_SETS, FALSE);
for (attr = __xml_first_child(attributes); attr; attr = __xml_next(attr)) {
const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
const char *value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
if (!name || !value) {
continue;
}
params = stonith_key_value_add(params, name, value);
}
data = create_device_registration_xml(rsc_id,
provider,
agent,
params);
if(force == FALSE && crm_element_value(match, XML_DIFF_MARKER)) {
stonith_device_register(data, NULL, TRUE);
} else {
stonith_device_remove(rsc_id, TRUE);
stonith_device_register(data, NULL, TRUE);
}
}
}
static void register_fencing_topology(xmlXPathObjectPtr xpathObj, gboolean force)
{
int max = 0, lpc = 0;
if(xpathObj && xpathObj->nodesetval) {
max = xpathObj->nodesetval->nodeNr;
}
for(lpc = 0; lpc < max; lpc++) {
int index = 0;
const char *target;
const char *dev_list;
stonith_key_value_t *devices = NULL;
xmlNode *match = getXpathResult(xpathObj, lpc);
CRM_CHECK(match != NULL, continue);
crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
target = crm_element_value(match, XML_ATTR_STONITH_TARGET);
dev_list = crm_element_value(match, XML_ATTR_STONITH_DEVICES);
devices = parse_device_list(dev_list);
crm_trace("Updating %s[%d] (%s) to %s", target, index, ID(match), dev_list);
if(target == NULL) {
crm_err("Invalid fencing target in element %s", ID(match));
} else if(index <= 0) {
crm_err("Invalid level for %s in element %s", target, ID(match));
} else if(force == FALSE && crm_element_value(match, XML_DIFF_MARKER)) {
/* Addition */
topology_register_helper(target, index, devices);
} else { /* Modification */
/* Remove then re-add */
topology_remove_helper(target, index);
topology_register_helper(target, index, devices);
}
stonith_key_value_freeall(devices, 1, 1);
}
}
/* Fencing
<diff crm_feature_set="3.0.6">
<diff-removed>
<fencing-topology>
<fencing-level id="f-p1.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="removed:top"/>
<fencing-level id="f-p1.2" target="pcmk-1" index="2" devices="power" __crm_diff_marker__="removed:top"/>
<fencing-level devices="disk,network" id="f-p2.1"/>
</fencing-topology>
</diff-removed>
<diff-added>
<fencing-topology>
<fencing-level id="f-p.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="added:top"/>
<fencing-level id="f-p2.1" target="pcmk-2" index="1" devices="disk,something"/>
<fencing-level id="f-p3.1" target="pcmk-2" index="2" devices="power" __crm_diff_marker__="added:top"/>
</fencing-topology>
</diff-added>
</diff>
*/
static void
fencing_topology_init(xmlNode * msg)
{
xmlXPathObjectPtr xpathObj = NULL;
const char *xpath = "//" XML_TAG_FENCING_LEVEL;
crm_trace("Pushing in stonith topology");
/* Grab everything */
xpathObj = xpath_search(msg, xpath);
register_fencing_topology(xpathObj, TRUE);
if(xpathObj) {
xmlXPathFreeObject(xpathObj);
}
}
static void
cib_stonith_devices_init(xmlNode *msg)
{
xmlXPathObjectPtr xpathObj = NULL;
const char *xpath = "//" XML_CIB_TAG_RESOURCE;
crm_trace("Pushing in stonith devices");
/* Grab everything */
xpathObj = xpath_search(msg, xpath);
if(xpathObj) {
register_cib_device(xpathObj, TRUE);
xmlXPathFreeObject(xpathObj);
}
}
static void
update_cib_stonith_devices(const char *event, xmlNode * msg)
{
const char *xpath_add = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_RESOURCE;
const char *xpath_del = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_RESOURCE;
xmlXPathObjectPtr xpath_obj = NULL;
/* process deletions */
xpath_obj = xpath_search(msg, xpath_del);
if (xpath_obj) {
remove_cib_device(xpath_obj);
xmlXPathFreeObject(xpath_obj);
}
/* process additions */
xpath_obj = xpath_search(msg, xpath_add);
if (xpath_obj) {
register_cib_device(xpath_obj, FALSE);
xmlXPathFreeObject(xpath_obj);
}
}
static void
update_fencing_topology(const char *event, xmlNode * msg)
{
const char *xpath;
xmlXPathObjectPtr xpathObj = NULL;
/* Process deletions (only) */
xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_FENCING_LEVEL;
xpathObj = xpath_search(msg, xpath);
remove_fencing_topology(xpathObj);
if(xpathObj) {
xmlXPathFreeObject(xpathObj);
}
/* Process additions and changes */
xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_TAG_FENCING_LEVEL;
xpathObj = xpath_search(msg, xpath);
register_fencing_topology(xpathObj, FALSE);
if(xpathObj) {
xmlXPathFreeObject(xpathObj);
}
}
static void
update_cib_cache_cb(const char *event, xmlNode * msg)
{
update_fencing_topology(event, msg);
update_cib_stonith_devices(event, msg);
}
static void
init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
fencing_topology_init(msg);
cib_stonith_devices_init(msg);
}
static void
stonith_shutdown(int nsig)
{
stonith_shutdown_flag = TRUE;
crm_info("Terminating with %d clients", g_hash_table_size(client_list));
if(mainloop != NULL && g_main_is_running(mainloop)) {
g_main_quit(mainloop);
} else {
stonith_cleanup();
crm_exit(EX_OK);
}
}
cib_t *cib = NULL;
static void
stonith_cleanup(void)
{
if(cib) {
cib->cmds->signoff(cib);
}
if(ipcs) {
qb_ipcs_destroy(ipcs);
}
crm_peer_destroy();
g_hash_table_destroy(client_list);
free(stonith_our_uname);
}
/* *INDENT-OFF* */
static struct crm_option long_options[] = {
{"stand-alone", 0, 0, 's'},
{"stand-alone-w-cpg", 0, 0, 'c'},
{"verbose", 0, 0, 'V'},
{"version", 0, 0, '$'},
{"help", 0, 0, '?'},
{0, 0, 0, 0}
};
/* *INDENT-ON* */
static void
setup_cib(void)
{
static void *cib_library = NULL;
static cib_t *(*cib_new_fn)(void) = NULL;
static const char *(*cib_err_fn)(int) = NULL;
int rc, retries = 0;
if(cib_library == NULL) {
cib_library = dlopen(CIB_LIBRARY, RTLD_LAZY);
}
if(cib_library && cib_new_fn == NULL) {
cib_new_fn = dlsym(cib_library, "cib_new");
}
if(cib_library && cib_err_fn == NULL) {
cib_err_fn = dlsym(cib_library, "pcmk_strerror");
}
if(cib_new_fn != NULL) {
cib = (*cib_new_fn)();
}
if(cib == NULL) {
crm_err("No connection to the CIB");
return;
}
do {
sleep(retries);
rc = cib->cmds->signon(cib, CRM_SYSTEM_CRMD, cib_command);
} while(rc == -ENOTCONN && ++retries < 5);
if (rc != pcmk_ok) {
crm_err("Could not connect to the CIB service: %d %p", rc, cib_err_fn);
} else if (pcmk_ok != cib->cmds->add_notify_callback(
cib, T_CIB_DIFF_NOTIFY, update_cib_cache_cb)) {
crm_err("Could not set CIB notification callback");
} else {
rc = cib->cmds->query(cib, NULL, NULL, cib_scope_local);
add_cib_op_callback(cib, rc, FALSE, NULL, init_cib_cache_cb);
crm_notice("Watching for stonith topology changes");
}
}
struct qb_ipcs_service_handlers ipc_callbacks =
{
.connection_accept = st_ipc_accept,
.connection_created = st_ipc_created,
.msg_process = st_ipc_dispatch,
.connection_closed = st_ipc_closed,
.connection_destroyed = st_ipc_destroy
};
static void
st_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
{
/*
* This is a hack until we can send to a nodeid and/or we fix node name lookups
* These messages are ignored in stonith_peer_callback()
*/
xmlNode *query = create_xml_node(NULL, "stonith_command");
crm_xml_add(query, F_XML_TAGNAME, "stonith_command");
crm_xml_add(query, F_TYPE, T_STONITH_NG);
crm_xml_add(query, F_STONITH_OPERATION, "poke");
crm_debug("Broadcasting our uname because of node %u", node->id);
send_cluster_message(NULL, crm_msg_stonith_ng, query, FALSE);
free_xml(query);
}
int
main(int argc, char ** argv)
{
int flag;
int rc = 0;
int lpc = 0;
int argerr = 0;
int option_index = 0;
crm_cluster_t cluster;
const char *actions[] = { "reboot", "off", "list", "monitor", "status" };
crm_log_init("stonith-ng", LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
crm_set_options(NULL, "mode [options]", long_options,
"Provides a summary of cluster's current state."
"\n\nOutputs varying levels of detail in a number of different formats.\n");
while (1) {
flag = crm_get_option(argc, argv, &option_index);
if (flag == -1) {
break;
}
switch(flag) {
case 'V':
crm_bump_log_level(argc, argv);
break;
case 's':
stand_alone = TRUE;
break;
case 'c':
stand_alone = FALSE;
no_cib_connect = TRUE;
break;
case '$':
case '?':
crm_help(flag, EX_OK);
break;
default:
++argerr;
break;
}
}
if(argc - optind == 1 && safe_str_eq("metadata", argv[optind])) {
printf("<?xml version=\"1.0\"?><!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n");
printf("<resource-agent name=\"stonithd\">\n");
printf(" <version>1.0</version>\n");
printf(" <longdesc lang=\"en\">This is a fake resource that details the instance attributes handled by stonithd.</longdesc>\n");
printf(" <shortdesc lang=\"en\">Options available for all stonith resources</shortdesc>\n");
printf(" <parameters>\n");
printf(" <parameter name=\"stonith-timeout\" unique=\"0\">\n");
printf(" <shortdesc lang=\"en\">How long to wait for the STONITH action to complete per a stonith device.</shortdesc>\n");
printf(" <longdesc lang=\"en\">Overrides the stonith-timeout cluster property</longdesc>\n");
printf(" <content type=\"time\" default=\"60s\"/>\n");
printf(" </parameter>\n");
printf(" <parameter name=\"priority\" unique=\"0\">\n");
printf(" <shortdesc lang=\"en\">The priority of the stonith resource. The lower the number, the higher the priority.</shortdesc>\n");
printf(" <content type=\"integer\" default=\"0\"/>\n");
printf(" </parameter>\n");
printf(" <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTARG);
printf(" <shortdesc lang=\"en\">Advanced use only: An alternate parameter to supply instead of 'port'</shortdesc>\n");
printf(" <longdesc lang=\"en\">Some devices do not support the standard 'port' parameter or may provide additional ones.\n"
"Use this to specify an alternate, device-specific, parameter that should indicate the machine to be fenced.\n"
"A value of 'none' can be used to tell the cluster not to supply any additional parameters.\n"
" </longdesc>\n");
printf(" <content type=\"string\" default=\"port\"/>\n");
printf(" </parameter>\n");
printf(" <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTMAP);
printf(" <shortdesc lang=\"en\">A mapping of host names to ports numbers for devices that do not support host names.</shortdesc>\n");
printf(" <longdesc lang=\"en\">Eg. node1:1;node2:2,3 would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2</longdesc>\n");
printf(" <content type=\"string\" default=\"\"/>\n");
printf(" </parameter>\n");
printf(" <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTLIST);
printf(" <shortdesc lang=\"en\">A list of machines controlled by this device (Optional unless %s=static-list).</shortdesc>\n", STONITH_ATTR_HOSTCHECK);
printf(" <content type=\"string\" default=\"\"/>\n");
printf(" </parameter>\n");
printf(" <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTCHECK);
printf(" <shortdesc lang=\"en\">How to determin which machines are controlled by the device.</shortdesc>\n");
printf(" <longdesc lang=\"en\">Allowed values: dynamic-list (query the device), static-list (check the %s attribute), none (assume every device can fence every machine)</longdesc>\n", STONITH_ATTR_HOSTLIST);
printf(" <content type=\"string\" default=\"dynamic-list\"/>\n");
printf(" </parameter>\n");
for(lpc = 0; lpc < DIMOF(actions); lpc++) {
printf(" <parameter name=\"pcmk_%s_action\" unique=\"0\">\n", actions[lpc]);
printf(" <shortdesc lang=\"en\">Advanced use only: An alternate command to run instead of '%s'</shortdesc>\n", actions[lpc]);
printf(" <longdesc lang=\"en\">Some devices do not support the standard commands or may provide additional ones.\n"
"Use this to specify an alternate, device-specific, command that implements the '%s' action.</longdesc>\n", actions[lpc]);
printf(" <content type=\"string\" default=\"%s\"/>\n", actions[lpc]);
printf(" </parameter>\n");
}
printf(" </parameters>\n");
printf("</resource-agent>\n");
return 0;
}
if (optind != argc) {
++argerr;
}
if (argerr) {
crm_help('?', EX_USAGE);
}
mainloop_add_signal(SIGTERM, stonith_shutdown);
crm_peer_init();
client_list = g_hash_table_new(crm_str_hash, g_str_equal);
if(stand_alone == FALSE) {
#if SUPPORT_HEARTBEAT
cluster.hb_conn = NULL;
cluster.hb_dispatch = stonith_peer_hb_callback;
cluster.destroy = stonith_peer_hb_destroy;
#endif
if(is_openais_cluster()) {
#if SUPPORT_COROSYNC
cluster.destroy = stonith_peer_ais_destroy;
cluster.cs_dispatch = stonith_peer_ais_callback;
#endif
}
if(crm_cluster_connect(&cluster) == FALSE) {
crm_crit("Cannot sign in to the cluster... terminating");
crm_exit(100);
} else {
stonith_our_uname = cluster.uname;
}
stonith_our_uname = cluster.uname;
if (no_cib_connect == FALSE) {
setup_cib();
}
} else {
stonith_our_uname = strdup("localhost");
}
crm_set_status_callback(&st_peer_update_callback);
device_list = g_hash_table_new_full(
crm_str_hash, g_str_equal, NULL, free_device);
topology = g_hash_table_new_full(
crm_str_hash, g_str_equal, NULL, free_topology_entry);
ipcs = mainloop_add_ipc_server("stonith-ng", QB_IPC_NATIVE, &ipc_callbacks);
+ if(ipcs == NULL) {
+ crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
+ crm_exit(100);
+ }
#if SUPPORT_STONITH_CONFIG
if (((stand_alone == TRUE)) && !(standalone_cfg_read_file(STONITH_NG_CONF_FILE))) {
standalone_cfg_commit();
}
#endif
- if(ipcs != NULL) {
- /* Create the mainloop and run it... */
- mainloop = g_main_new(FALSE);
- crm_info("Starting %s mainloop", crm_system_name);
-
- g_main_run(mainloop);
-
- } else {
- crm_err("Couldnt start all communication channels, exiting.");
- }
+ /* Create the mainloop and run it... */
+ mainloop = g_main_new(FALSE);
+ crm_info("Starting %s mainloop", crm_system_name);
+ g_main_run(mainloop);
stonith_cleanup();
#if SUPPORT_HEARTBEAT
if(cluster.hb_conn) {
cluster.hb_conn->llc_ops->delete(cluster.hb_conn);
}
#endif
crm_info("Done");
return crm_exit(rc);
}
diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c
index 6c63bcdc14..51b589d721 100644
--- a/lib/cib/cib_client.c
+++ b/lib/cib/cib_client.c
@@ -1,626 +1,618 @@
/*
* Copyright (c) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#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/msg_xml.h>
#include <crm/common/xml.h>
GHashTable *cib_op_callback_table = NULL;
int cib_client_set_op_callback(cib_t * cib, void (*callback) (const xmlNode * msg, int call_id,
int rc, xmlNode * output));
int cib_client_add_notify_callback(cib_t * cib, const char *event,
void (*callback) (const char *event, xmlNode * msg));
int cib_client_del_notify_callback(cib_t * cib, const char *event,
void (*callback) (const char *event, xmlNode * msg));
gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b);
#define op_common(cib) do { \
if(cib == NULL) { \
return -EINVAL; \
} else if(cib->delegate_fn == NULL) { \
return -EPROTONOSUPPORT; \
} \
} while(0)
static int
cib_client_noop(cib_t * cib, int call_options)
{
op_common(cib);
return cib_internal_op(cib, CRM_OP_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, CIB_OP_QUERY, host, section, NULL, output_data, call_options, NULL);
}
static int
cib_client_is_master(cib_t * cib)
{
op_common(cib);
return cib_internal_op(cib, CIB_OP_ISMASTER, NULL, NULL, NULL, NULL,
cib_scope_local | cib_sync_call, NULL);
}
static int
cib_client_set_slave(cib_t * cib, int call_options)
{
op_common(cib);
return cib_internal_op(cib, CIB_OP_SLAVE, NULL, NULL, NULL, NULL, call_options, NULL);
}
static int
cib_client_set_slave_all(cib_t * cib, int call_options)
{
return -EPROTONOSUPPORT;
}
static int
cib_client_set_master(cib_t * cib, int call_options)
{
op_common(cib);
crm_trace("Adding cib_scope_local to options");
return cib_internal_op(cib, CIB_OP_MASTER, 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, CIB_OP_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, CIB_OP_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, CIB_OP_SYNC, 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, CIB_OP_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, CIB_OP_MODIFY, NULL, section, data, NULL, call_options, NULL);
}
static int
cib_client_update(cib_t * cib, const char *section, xmlNode * data, int call_options)
{
op_common(cib);
return cib_internal_op(cib, CIB_OP_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, CIB_OP_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, CIB_OP_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, CIB_OP_DELETE_ALT, 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, CIB_OP_ERASE, NULL, NULL, NULL, output_data, call_options, NULL);
}
-static int
-cib_client_quit(cib_t * cib, int call_options)
-{
- op_common(cib);
- return cib_internal_op(cib, CRM_OP_QUIT, NULL, NULL, NULL, NULL, 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);
free(blob);
}
char *
get_shadow_file(const char *suffix)
{
char *cib_home = NULL;
char *fullname = NULL;
char *name = crm_concat("shadow", 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 {
crm_perror(LOG_ERR, "Cannot get password entry for uid: %d", uid);
user = getenv("USER");
}
if (safe_str_eq(user, "root") || safe_str_eq(user, CRM_DAEMON_USER)) {
dir = CRM_CONFIG_DIR;
} else {
const char *home = NULL;
if ((home = getenv("HOME")) == NULL) {
if (pwent) {
home = pwent->pw_dir;
}
}
if ((dir = getenv("TMPDIR")) == NULL) {
dir = "/tmp";
}
if (home && home[0] == '/') {
int rc = 0;
cib_home = crm_concat(home, ".cib", '/');
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_concat(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;
}
cib_t *
cib_new_no_shadow(void)
{
unsetenv("CIB_shadow");
return cib_new();
}
cib_t *
cib_new(void)
{
const char *value = getenv("CIB_shadow");
if (value) {
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;
int port = crm_parse_int(value, NULL);
const char *server = getenv("CIB_server");
const char *user = getenv("CIB_user");
const char *pass = getenv("CIB_passwd");
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();
}
/* this is backwards...
cib_*_new should call this not the other way around
*/
cib_t *
cib_new_variant(void)
{
cib_t *new_cib = NULL;
new_cib = calloc(1, sizeof(cib_t));
if (cib_op_callback_table != NULL) {
g_hash_table_destroy(cib_op_callback_table);
cib_op_callback_table = NULL;
}
if (cib_op_callback_table == NULL) {
cib_op_callback_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, cib_destroy_op_callback);
}
new_cib->call_id = 1;
new_cib->variant = cib_undefined;
new_cib->type = cib_none;
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));
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->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 = cib_client_is_master;
new_cib->cmds->set_master = cib_client_set_master;
new_cib->cmds->set_slave = cib_client_set_slave;
new_cib->cmds->set_slave_all = cib_client_set_slave_all;
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_update;
new_cib->cmds->replace = cib_client_replace;
new_cib->cmds->delete = cib_client_delete;
new_cib->cmds->erase = cib_client_erase;
- new_cib->cmds->quit = cib_client_quit;
new_cib->cmds->delete_absolute = cib_client_delete_absolute;
return new_cib;
}
void
cib_delete(cib_t * 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);
}
g_hash_table_destroy(cib_op_callback_table);
cib_op_callback_table = NULL;
cib->cmds->free(cib);
cib = NULL;
}
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;
}
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 -ENOTUNIQ;
} 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;
}
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;
}
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);
cib->cmds->register_notification(cib, event, 0);
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");
}
free(new_client);
return pcmk_ok;
}
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 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);
/* Always return TRUE, never remove the handler
* We do that in remove_cib_op_callback()
*/
return TRUE;
}
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 *))
{
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));
}
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;
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);
g_hash_table_insert(cib_op_callback_table, GINT_TO_POINTER(call_id), blob);
return TRUE;
}
void
remove_cib_op_callback(int call_id, gboolean all_callbacks)
{
if (all_callbacks) {
if (cib_op_callback_table != NULL) {
g_hash_table_destroy(cib_op_callback_table);
}
cib_op_callback_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, cib_destroy_op_callback);
} else {
g_hash_table_remove(cib_op_callback_table, GINT_TO_POINTER(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, crm_str(blob->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);
}
diff --git a/lrmd/lrmd.c b/lrmd/lrmd.c
index 69fc3bad9b..3a7f638072 100644
--- a/lrmd/lrmd.c
+++ b/lrmd/lrmd.c
@@ -1,1167 +1,1164 @@
/*
* Copyright (c) 2012 David Vossel <dvossel@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <crm_internal.h>
#include <glib.h>
#include <unistd.h>
#include <crm/crm.h>
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/common/ipc.h>
#include <crm/msg_xml.h>
#include <lrmd_private.h>
#ifdef HAVE_SYS_TIMEB_H
# include <sys/timeb.h>
#endif
GHashTable *rsc_list = NULL;
GHashTable *client_list = NULL;
typedef struct lrmd_cmd_s {
int timeout;
int interval;
int start_delay;
int call_id;
int exec_rc;
int lrmd_op_status;
int call_opts;
/* Timer ids, must be removed on cmd destruction. */
int delay_id;
int stonith_recurring_id;
int rsc_deleted;
char *client_id;
char *origin;
char *rsc_id;
char *action;
char *output;
char *userdata_str;
#ifdef HAVE_SYS_TIMEB_H
/* Timestamp of when op ran */
struct timeb t_run;
/* Timestamp of when op was queued */
struct timeb t_queue;
/* Timestamp of last rc change */
struct timeb t_rcchange;
#endif
int first_notify_sent;
int last_notify_rc;
int last_notify_op_status;
GHashTable *params;
} lrmd_cmd_t;
static void cmd_finalize(lrmd_cmd_t * cmd, lrmd_rsc_t * rsc);
static gboolean lrmd_rsc_dispatch(gpointer user_data);
static void cancel_all_recurring(lrmd_rsc_t *rsc, const char *client_id);
static lrmd_rsc_t *
build_rsc_from_xml(xmlNode * msg)
{
xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, msg, LOG_ERR);
lrmd_rsc_t *rsc = NULL;
rsc = calloc(1, sizeof(lrmd_rsc_t));
crm_element_value_int(msg, F_LRMD_CALLOPTS, &rsc->call_opts);
rsc->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID);
rsc->class = crm_element_value_copy(rsc_xml, F_LRMD_CLASS);
rsc->provider = crm_element_value_copy(rsc_xml, F_LRMD_PROVIDER);
rsc->type = crm_element_value_copy(rsc_xml, F_LRMD_TYPE);
rsc->work = mainloop_add_trigger(G_PRIORITY_HIGH, lrmd_rsc_dispatch, rsc);
return rsc;
}
static lrmd_cmd_t *
create_lrmd_cmd(xmlNode * msg, lrmd_client_t * client)
{
int call_options = 0;
xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, msg, LOG_ERR);
lrmd_cmd_t *cmd = NULL;
cmd = calloc(1, sizeof(lrmd_cmd_t));
crm_element_value_int(msg, F_LRMD_CALLOPTS, &call_options);
cmd->call_opts = call_options;
cmd->client_id = strdup(client->id);
crm_element_value_int(msg, F_LRMD_CALLID, &cmd->call_id);
crm_element_value_int(rsc_xml, F_LRMD_RSC_INTERVAL, &cmd->interval);
crm_element_value_int(rsc_xml, F_LRMD_TIMEOUT, &cmd->timeout);
crm_element_value_int(rsc_xml, F_LRMD_RSC_START_DELAY, &cmd->start_delay);
cmd->origin = crm_element_value_copy(rsc_xml, F_LRMD_ORIGIN);
cmd->action = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ACTION);
cmd->userdata_str = crm_element_value_copy(rsc_xml, F_LRMD_RSC_USERDATA_STR);
cmd->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID);
cmd->params = xml2list(rsc_xml);
return cmd;
}
static void
free_lrmd_cmd(lrmd_cmd_t * cmd)
{
if (cmd->stonith_recurring_id) {
g_source_remove(cmd->stonith_recurring_id);
}
if (cmd->delay_id) {
g_source_remove(cmd->delay_id);
}
if (cmd->params) {
g_hash_table_destroy(cmd->params);
}
free(cmd->origin);
free(cmd->action);
free(cmd->userdata_str);
free(cmd->rsc_id);
free(cmd->output);
free(cmd->client_id);
free(cmd);
}
static gboolean
stonith_recurring_op_helper(gpointer data)
{
lrmd_cmd_t *cmd = data;
lrmd_rsc_t *rsc;
cmd->stonith_recurring_id = 0;
if (!cmd->rsc_id) {
return FALSE;
}
rsc = g_hash_table_lookup(rsc_list, cmd->rsc_id);
CRM_ASSERT(rsc != NULL);
/* take it out of recurring_ops list, and put it in the pending ops
* to be executed */
rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd);
rsc->pending_ops = g_list_append(rsc->pending_ops, cmd);
#ifdef HAVE_SYS_TIMEB_H
ftime(&cmd->t_queue);
#endif
mainloop_set_trigger(rsc->work);
return FALSE;
}
static gboolean
start_delay_helper(gpointer data)
{
lrmd_cmd_t *cmd = data;
lrmd_rsc_t *rsc = NULL;
cmd->delay_id = 0;
rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL;
if (rsc) {
mainloop_set_trigger(rsc->work);
}
return FALSE;
}
static void
schedule_lrmd_cmd(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
{
CRM_CHECK(cmd != NULL, return);
CRM_CHECK(rsc != NULL, return);
crm_trace("Scheduling %s on %s", cmd->action, rsc->rsc_id);
rsc->pending_ops = g_list_append(rsc->pending_ops, cmd);
#ifdef HAVE_SYS_TIMEB_H
ftime(&cmd->t_queue);
#endif
mainloop_set_trigger(rsc->work);
if (cmd->start_delay) {
cmd->delay_id = g_timeout_add(cmd->start_delay, start_delay_helper, cmd);
}
}
static void
send_reply(lrmd_client_t * client, int rc, uint32_t id, int call_id)
{
int send_rc = 0;
xmlNode *reply = NULL;
reply = create_xml_node(NULL, T_LRMD_REPLY);
crm_xml_add(reply, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add_int(reply, F_LRMD_RC, rc);
crm_xml_add_int(reply, F_LRMD_CALLID, call_id);
send_rc = crm_ipcs_send(client->channel, id, reply, FALSE);
free_xml(reply);
if (send_rc < 0) {
crm_warn("LRMD reply to %s failed: %d", client->name, send_rc);
}
}
static void
send_client_notify(gpointer key, gpointer value, gpointer user_data)
{
xmlNode *update_msg = user_data;
lrmd_client_t *client = value;
if (client == NULL) {
crm_err("Asked to send event to NULL client");
return;
} else if (client->channel == NULL) {
crm_trace("Asked to send event to disconnected client");
return;
} else if (client->name == NULL) {
crm_trace("Asked to send event to client with no name");
return;
}
if (crm_ipcs_send(client->channel, 0, update_msg, TRUE) <= 0) {
crm_warn("Notification of client %s/%s failed", client->name, client->id);
}
}
#ifdef HAVE_SYS_TIMEB_H
static int
time_diff_ms(struct timeb *now, struct timeb *old)
{
int sec = difftime(now->time, old->time);
int ms = now->millitm - old->millitm;
if (old->time == 0) {
return 0;
}
return (sec * 1000) + ms;
}
#endif
static void
send_cmd_complete_notify(lrmd_cmd_t * cmd)
{
#ifdef HAVE_SYS_TIMEB_H
struct timeb now = { 0, };
#endif
xmlNode *notify = NULL;
/* if the first notify result for a cmd has already been sent earlier, and the
* the option to only send notifies on result changes is set. Check to see
* if the last result is the same as the new one. If so, suppress this update */
if (cmd->first_notify_sent && (cmd->call_opts & lrmd_opt_notify_changes_only)) {
if (cmd->last_notify_rc == cmd->exec_rc &&
cmd->last_notify_op_status == cmd->lrmd_op_status) {
/* only send changes */
return;
}
}
cmd->first_notify_sent = 1;
cmd->last_notify_rc = cmd->exec_rc;
cmd->last_notify_op_status = cmd->lrmd_op_status;
notify = create_xml_node(NULL, T_LRMD_NOTIFY);
crm_xml_add(notify, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add_int(notify, F_LRMD_TIMEOUT, cmd->timeout);
crm_xml_add_int(notify, F_LRMD_RSC_INTERVAL, cmd->interval);
crm_xml_add_int(notify, F_LRMD_RSC_START_DELAY, cmd->start_delay);
crm_xml_add_int(notify, F_LRMD_EXEC_RC, cmd->exec_rc);
crm_xml_add_int(notify, F_LRMD_OP_STATUS, cmd->lrmd_op_status);
crm_xml_add_int(notify, F_LRMD_CALLID, cmd->call_id);
crm_xml_add_int(notify, F_LRMD_RSC_DELETED, cmd->rsc_deleted);
#ifdef HAVE_SYS_TIMEB_H
ftime(&now);
crm_xml_add_int(notify, F_LRMD_RSC_RUN_TIME, cmd->t_run.time);
crm_xml_add_int(notify, F_LRMD_RSC_RCCHANGE_TIME, cmd->t_rcchange.time);
crm_xml_add_int(notify, F_LRMD_RSC_EXEC_TIME, time_diff_ms(&now, &cmd->t_run));
crm_xml_add_int(notify, F_LRMD_RSC_QUEUE_TIME, time_diff_ms(&cmd->t_run, &cmd->t_queue));
#endif
crm_xml_add(notify, F_LRMD_OPERATION, LRMD_OP_RSC_EXEC);
crm_xml_add(notify, F_LRMD_RSC_ID, cmd->rsc_id);
crm_xml_add(notify, F_LRMD_RSC_ACTION, cmd->action);
crm_xml_add(notify, F_LRMD_RSC_USERDATA_STR, cmd->userdata_str);
crm_xml_add(notify, F_LRMD_RSC_OUTPUT, cmd->output);
if (cmd->params) {
char *key = NULL;
char *value = NULL;
GHashTableIter iter;
xmlNode *args = create_xml_node(notify, XML_TAG_ATTRS);
g_hash_table_iter_init(&iter, cmd->params);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
hash2field((gpointer) key, (gpointer) value, args);
}
}
if (cmd->client_id && (cmd->call_opts & lrmd_opt_notify_orig_only)) {
lrmd_client_t *client = g_hash_table_lookup(client_list, cmd->client_id);
if (client) {
send_client_notify(client->id, client, notify);
}
} else {
g_hash_table_foreach(client_list, send_client_notify, notify);
}
free_xml(notify);
}
static void
send_generic_notify(int rc, xmlNode * request)
{
int call_id = 0;
xmlNode *notify = NULL;
xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
const char *op = crm_element_value(request, F_LRMD_OPERATION);
crm_element_value_int(request, F_LRMD_CALLID, &call_id);
notify = create_xml_node(NULL, T_LRMD_NOTIFY);
crm_xml_add(notify, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add_int(notify, F_LRMD_RC, rc);
crm_xml_add_int(notify, F_LRMD_CALLID, call_id);
crm_xml_add(notify, F_LRMD_OPERATION, op);
crm_xml_add(notify, F_LRMD_RSC_ID, rsc_id);
g_hash_table_foreach(client_list, send_client_notify, notify);
free_xml(notify);
}
static void
cmd_finalize(lrmd_cmd_t * cmd, lrmd_rsc_t * rsc)
{
crm_trace("Resource operation rsc:%s action:%s completed (%p %p)", cmd->rsc_id, cmd->action, rsc?rsc->active:NULL, cmd);
if (rsc && (rsc->active == cmd)) {
rsc->active = NULL;
mainloop_set_trigger(rsc->work);
}
if (!rsc) {
cmd->rsc_deleted = 1;
}
send_cmd_complete_notify(cmd);
/* crmd expects lrmd to automatically cancel recurring ops after rsc stops */
if (rsc && safe_str_eq(cmd->action, "stop")) {
cancel_all_recurring(rsc, NULL);
}
if (cmd->interval && (cmd->lrmd_op_status == PCMK_LRM_OP_CANCELLED)) {
if (rsc) {
rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd);
rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd);
}
free_lrmd_cmd(cmd);
} else if (cmd->interval == 0) {
if (rsc) {
rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd);
}
free_lrmd_cmd(cmd);
} else {
/* Clear all the values pertaining just to the last iteration of a recurring op. */
cmd->lrmd_op_status = 0;
memset(&cmd->t_run, 0, sizeof(cmd->t_run));
memset(&cmd->t_queue, 0, sizeof(cmd->t_queue));
free(cmd->output);
cmd->output = NULL;
}
}
static int
lsb2uniform_rc(const char *action, int rc)
{
if (rc < 0) {
return PCMK_EXECRA_UNKNOWN_ERROR;
}
/* status has different return codes that everything else. */
if (!safe_str_eq(action, "status") && !safe_str_eq(action, "monitor")) {
if (rc > PCMK_LSB_NOT_RUNNING) {
return PCMK_EXECRA_UNKNOWN_ERROR;
}
return rc;
}
switch (rc) {
case PCMK_LSB_STATUS_OK:
return PCMK_EXECRA_OK;
case PCMK_LSB_STATUS_NOT_INSTALLED:
return PCMK_EXECRA_NOT_INSTALLED;
case PCMK_LSB_STATUS_VAR_PID:
case PCMK_LSB_STATUS_VAR_LOCK:
case PCMK_LSB_STATUS_NOT_RUNNING:
return PCMK_EXECRA_NOT_RUNNING;
default:
return PCMK_EXECRA_UNKNOWN_ERROR;
}
return PCMK_EXECRA_UNKNOWN_ERROR;
}
static int
ocf2uniform_rc(int rc)
{
if (rc < 0 || rc > PCMK_OCF_FAILED_MASTER) {
return PCMK_EXECRA_UNKNOWN_ERROR;
}
return rc;
}
static int
stonith2uniform_rc(const char *action, int rc)
{
if (rc == -ENODEV) {
if (safe_str_eq(action, "stop")) {
rc = PCMK_EXECRA_OK;
} else if (safe_str_eq(action, "start")) {
rc = PCMK_EXECRA_NOT_INSTALLED;
} else {
rc = PCMK_EXECRA_NOT_RUNNING;
}
} else if (rc != 0) {
rc = PCMK_EXECRA_UNKNOWN_ERROR;
}
return rc;
}
static int
get_uniform_rc(const char *standard, const char *action, int rc)
{
if (safe_str_eq(standard, "ocf")) {
return ocf2uniform_rc(rc);
} else if (safe_str_eq(standard, "stonith")) {
return stonith2uniform_rc(action, rc);
} else if (safe_str_eq(standard, "systemd")) {
return rc;
} else if (safe_str_eq(standard, "upstart")) {
return rc;
} else {
return lsb2uniform_rc(action, rc);
}
}
void
client_disconnect_cleanup(const char *client_id)
{
GHashTableIter iter;
lrmd_rsc_t *rsc = NULL;
char *key = NULL;
g_hash_table_iter_init(&iter, rsc_list);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & rsc)) {
if (rsc->call_opts & lrmd_opt_drop_recurring) {
/* This client is disconnecting, drop any recurring operations
* it may have initiated on the resource */
cancel_all_recurring(rsc, client_id);
}
}
}
static void
action_complete(svc_action_t * action)
{
lrmd_rsc_t *rsc;
lrmd_cmd_t *cmd = action->cb_data;
if (!cmd) {
crm_err("LRMD action (%s) completed does not match any known operations.", action->id);
return;
}
#ifdef HAVE_SYS_TIMEB_H
if (cmd->exec_rc != action->rc) {
ftime(&cmd->t_rcchange);
}
#endif
cmd->exec_rc = get_uniform_rc(action->standard, cmd->action, action->rc);
cmd->lrmd_op_status = action->status;
rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL;
if (action->stdout_data) {
cmd->output = strdup(action->stdout_data);
}
cmd_finalize(cmd, rsc);
}
static void
stonith_action_complete(lrmd_cmd_t *cmd, int rc)
{
int recurring = cmd->interval;
lrmd_rsc_t *rsc = NULL;
cmd->exec_rc = get_uniform_rc("stonith", cmd->action, rc);
rsc = g_hash_table_lookup(rsc_list, cmd->rsc_id);
if (cmd->lrmd_op_status == PCMK_LRM_OP_CANCELLED) {
recurring = 0;
/* do nothing */
} else if (rc) {
/* Attempt to map return codes to op status if possible */
switch (rc) {
case -EPROTONOSUPPORT:
cmd->lrmd_op_status = PCMK_LRM_OP_NOTSUPPORTED;
break;
case -ETIME:
cmd->lrmd_op_status = PCMK_LRM_OP_TIMEOUT;
break;
default:
cmd->lrmd_op_status = PCMK_LRM_OP_ERROR;
}
} else {
/* command successful */
cmd->lrmd_op_status = PCMK_LRM_OP_DONE;
if (safe_str_eq(cmd->action, "start") && rsc) {
rsc->stonith_started = 1;
}
}
if (recurring && rsc) {
if (cmd->stonith_recurring_id) {
g_source_remove(cmd->stonith_recurring_id);
}
cmd->stonith_recurring_id = g_timeout_add(cmd->interval, stonith_recurring_op_helper, cmd);
}
cmd_finalize(cmd, rsc);
}
static void
lrmd_stonith_callback(stonith_t * stonith, stonith_callback_data_t *data)
{
stonith_action_complete(data->userdata, data->rc);
}
void
stonith_connection_failed(void)
{
GHashTableIter iter;
GList *cmd_list = NULL;
GList *cmd_iter = NULL;
lrmd_rsc_t *rsc = NULL;
char *key = NULL;
g_hash_table_iter_init(&iter, rsc_list);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & rsc)) {
if (safe_str_eq(rsc->class, "stonith")) {
if (rsc->recurring_ops) {
cmd_list = g_list_concat(cmd_list, rsc->recurring_ops);
}
if (rsc->pending_ops) {
cmd_list = g_list_concat(cmd_list, rsc->pending_ops);
}
rsc->pending_ops = rsc->recurring_ops = NULL;
}
}
if (!cmd_list) {
return;
}
crm_err("STONITH connection failed, finalizing %d pending operations.", g_list_length(cmd_list));
for (cmd_iter = cmd_list; cmd_iter; cmd_iter = cmd_iter->next) {
stonith_action_complete(cmd_iter->data, -ENOTCONN);
}
g_list_free(cmd_list);
}
static int
lrmd_rsc_execute_stonith(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
{
int rc = 0;
int do_monitor = 0;
stonith_t *stonith_api = get_stonith_connection();
if (!stonith_api) {
cmd->exec_rc = get_uniform_rc("stonith", cmd->action, -ENOTCONN);
cmd->lrmd_op_status = PCMK_LRM_OP_ERROR;
cmd_finalize(cmd, rsc);
return -EUNATCH;
}
if (safe_str_eq(cmd->action, "start")) {
char *key = NULL;
char *value = NULL;
stonith_key_value_t *device_params = NULL;
if (cmd->params) {
GHashTableIter iter;
g_hash_table_iter_init(&iter, cmd->params);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
device_params = stonith_key_value_add(device_params, key, value);
}
}
/* Stonith automatically registers devices from the CIB when changes occur,
* but to avoid a possible race condition between stonith receiving the CIB update
* and the lrmd requesting that resource, the lrmd still registers the device as well.
* Stonith knows how to handle duplicate device registrations correctly. */
rc = stonith_api->cmds->register_device(stonith_api,
st_opt_sync_call,
cmd->rsc_id,
rsc->provider, rsc->type, device_params);
stonith_key_value_freeall(device_params, 1, 1);
if (rc == 0) {
do_monitor = 1;
}
} else if (safe_str_eq(cmd->action, "stop")) {
rc = stonith_api->cmds->remove_device(stonith_api, st_opt_sync_call, cmd->rsc_id);
rsc->stonith_started = 0;
} else if (safe_str_eq(cmd->action, "monitor")) {
if (cmd->interval) {
do_monitor = 1;
} else {
rc = rsc->stonith_started ? 0 : -ENODEV;
}
}
if (!do_monitor) {
goto cleanup_stonith_exec;
}
rc = stonith_api->cmds->monitor(stonith_api,
0, cmd->rsc_id, cmd->timeout / 1000 );
rc = stonith_api->cmds->register_callback(
stonith_api,
rc,
0,
0,
cmd,
"lrmd_stonith_callback",
lrmd_stonith_callback);
/* don't cleanup yet, we will find out the result of the monitor later */
if (rc > 0) {
rsc->active = cmd;
return rc;
} else if (rc == 0) {
rc = -1;
}
cleanup_stonith_exec:
stonith_action_complete(cmd, rc);
return rc;
}
static const char *
normalize_action_name(lrmd_rsc_t * rsc, const char *action)
{
if (safe_str_eq(action, "monitor") &&
(safe_str_eq(rsc->class, "lsb") ||
safe_str_eq(rsc->class, "service") || safe_str_eq(rsc->class, "systemd"))) {
return "status";
}
return action;
}
static void
dup_attr(gpointer key, gpointer value, gpointer user_data)
{
g_hash_table_replace(user_data, strdup(key), strdup(value));
}
static int
lrmd_rsc_execute_service_lib(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
{
svc_action_t *action = NULL;
GHashTable *params_copy = NULL;
CRM_ASSERT(rsc);
CRM_ASSERT(cmd);
crm_trace("Creating action, resource:%s action:%s class:%s provider:%s agent:%s",
rsc->rsc_id, cmd->action, rsc->class, rsc->provider, rsc->type);
if (cmd->params) {
params_copy = g_hash_table_new_full(crm_str_hash,
g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
if (params_copy != NULL) {
g_hash_table_foreach(cmd->params, dup_attr, params_copy);
}
}
action = resources_action_create(rsc->rsc_id,
rsc->class,
rsc->provider,
rsc->type,
normalize_action_name(rsc, cmd->action),
cmd->interval, cmd->timeout, params_copy);
if (!action) {
crm_err("Failed to create action, action:%s on resource %s", cmd->action, rsc->rsc_id);
cmd->lrmd_op_status = PCMK_LRM_OP_ERROR;
goto exec_done;
}
action->cb_data = cmd;
/* 'cmd' may not be valid after this point
*
* Upstart and systemd both synchronously determine monitor/status
* results and call action_complete (which may free 'cmd') if necessary
*/
if (services_action_async(action, action_complete)) {
return TRUE;
}
cmd->exec_rc = action->rc;
cmd->lrmd_op_status = PCMK_LRM_OP_ERROR;
services_action_free(action);
action = NULL;
exec_done:
cmd_finalize(cmd, rsc);
return TRUE;
}
static gboolean
lrmd_rsc_execute(lrmd_rsc_t * rsc)
{
lrmd_cmd_t *cmd = NULL;
CRM_CHECK(rsc != NULL, return FALSE);
if (rsc->active) {
crm_trace("%s is still active", rsc->rsc_id);
return TRUE;
}
if (rsc->pending_ops) {
GList *first = rsc->pending_ops;
cmd = first->data;
if (cmd->delay_id) {
crm_trace
("Command %s %s was asked to run too early, waiting for start_delay timeout of %dms",
cmd->rsc_id, cmd->action, cmd->start_delay);
return TRUE;
}
rsc->pending_ops = g_list_remove_link(rsc->pending_ops, first);
g_list_free_1(first);
#ifdef HAVE_SYS_TIMEB_H
ftime(&cmd->t_run);
}
#endif
if (!cmd) {
crm_trace("Nothing further to do for %s", rsc->rsc_id);
return TRUE;
}
rsc->active = cmd; /* only one op at a time for a rsc */
if (cmd->interval) {
rsc->recurring_ops = g_list_append(rsc->recurring_ops, cmd);
}
if (safe_str_eq(rsc->class, "stonith")) {
lrmd_rsc_execute_stonith(rsc, cmd);
} else {
lrmd_rsc_execute_service_lib(rsc, cmd);
}
return TRUE;
}
static gboolean
lrmd_rsc_dispatch(gpointer user_data)
{
return lrmd_rsc_execute(user_data);
}
void
free_rsc(gpointer data)
{
GListPtr gIter = NULL;
lrmd_rsc_t *rsc = data;
int is_stonith = safe_str_eq(rsc->class, "stonith");
for (gIter = rsc->pending_ops; gIter != NULL; gIter = gIter->next) {
lrmd_cmd_t *cmd = gIter->data;
/* command was never executed */
cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED;
cmd_finalize(cmd, NULL);
}
/* frees list, but not list elements. */
g_list_free(rsc->pending_ops);
for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) {
lrmd_cmd_t *cmd = gIter->data;
if (is_stonith) {
cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED;
cmd_finalize(cmd, NULL);
} else {
/* This command is already handed off to service library,
* let service library cancel it and tell us via the callback
* when it is cancelled. The rsc can be safely destroyed
* even if we are waiting for the cancel result */
services_action_cancel(rsc->rsc_id, cmd->action, cmd->interval);
}
}
/* frees list, but not list elements. */
g_list_free(rsc->recurring_ops);
free(rsc->rsc_id);
free(rsc->class);
free(rsc->provider);
free(rsc->type);
mainloop_destroy_trigger(rsc->work);
free(rsc);
}
static int
process_lrmd_signon(lrmd_client_t * client, uint32_t id, xmlNode * request)
{
xmlNode *reply = create_xml_node(NULL, "reply");
crm_xml_add(reply, F_LRMD_OPERATION, CRM_OP_REGISTER);
crm_xml_add(reply, F_LRMD_CLIENTID, client->id);
crm_ipcs_send(client->channel, id, reply, FALSE);
free_xml(reply);
return pcmk_ok;
}
static int
process_lrmd_rsc_register(lrmd_client_t * client, uint32_t id, xmlNode * request)
{
int rc = pcmk_ok;
lrmd_rsc_t *rsc = build_rsc_from_xml(request);
lrmd_rsc_t *dup = g_hash_table_lookup(rsc_list, rsc->rsc_id);
if (dup &&
safe_str_eq(rsc->class, dup->class) &&
safe_str_eq(rsc->provider, dup->provider) && safe_str_eq(rsc->type, dup->type)) {
crm_warn("Can't add, RSC '%s' already present in the rsc list (%d active resources)",
rsc->rsc_id, g_hash_table_size(rsc_list));
free_rsc(rsc);
return rc;
}
g_hash_table_replace(rsc_list, rsc->rsc_id, rsc);
crm_info("Added '%s' to the rsc list (%d active resources)",
rsc->rsc_id, g_hash_table_size(rsc_list));
return rc;
}
static void
process_lrmd_get_rsc_info(lrmd_client_t * client, uint32_t id, xmlNode * request)
{
int rc = pcmk_ok;
int send_rc = 0;
int call_id = 0;
xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
xmlNode *reply = NULL;
lrmd_rsc_t *rsc = NULL;
crm_element_value_int(request, F_LRMD_CALLID, &call_id);
if (!rsc_id) {
rc = -ENODEV;
goto get_rsc_done;
}
if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) {
crm_info("Resource '%s' not found (%d active resources)",
rsc_id, g_hash_table_size(rsc_list));
rc = -ENODEV;
goto get_rsc_done;
}
get_rsc_done:
reply = create_xml_node(NULL, T_LRMD_REPLY);
crm_xml_add(reply, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add_int(reply, F_LRMD_RC, rc);
crm_xml_add_int(reply, F_LRMD_CALLID, call_id);
if (rsc) {
crm_xml_add(reply, F_LRMD_RSC_ID, rsc->rsc_id);
crm_xml_add(reply, F_LRMD_CLASS, rsc->class);
crm_xml_add(reply, F_LRMD_PROVIDER, rsc->provider);
crm_xml_add(reply, F_LRMD_TYPE, rsc->type);
}
send_rc = crm_ipcs_send(client->channel, id, reply, FALSE);
if (send_rc < 0) {
crm_warn("LRMD reply to %s failed: %d", client->name, send_rc);
}
free_xml(reply);
}
static int
process_lrmd_rsc_unregister(lrmd_client_t * client, uint32_t id, xmlNode * request)
{
int rc = pcmk_ok;
lrmd_rsc_t *rsc = NULL;
xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
if (!rsc_id) {
return -ENODEV;
}
if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) {
crm_info("Resource '%s' not found (%d active resources)",
rsc_id, g_hash_table_size(rsc_list));
return pcmk_ok;
}
if (rsc->active) {
/* let the caller know there are still active ops on this rsc to watch for */
crm_trace("Operation still in progress: %p", rsc->active);
rc = -EINPROGRESS;
}
g_hash_table_remove(rsc_list, rsc_id);
return rc;
}
static int
process_lrmd_rsc_exec(lrmd_client_t * client, uint32_t id, xmlNode * request)
{
lrmd_rsc_t *rsc = NULL;
lrmd_cmd_t *cmd = NULL;
xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
if (!rsc_id) {
return -EINVAL;
}
if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) {
crm_info("Resource '%s' not found (%d active resources)",
rsc_id, g_hash_table_size(rsc_list));
return -ENODEV;
}
cmd = create_lrmd_cmd(request, client);
schedule_lrmd_cmd(rsc, cmd);
return cmd->call_id;
}
static int
cancel_op(const char *rsc_id, const char *action, int interval)
{
GListPtr gIter = NULL;
lrmd_rsc_t *rsc = g_hash_table_lookup(rsc_list, rsc_id);
/* How to cancel an action.
* 1. Check pending ops list, if it hasn't been handed off
* to the service library or stonith recurring list remove
* it there and that will stop it.
* 2. If it isn't in the pending ops list, then its either a
* recurring op in the stonith recurring list, or the service
* library's recurring list. Stop it there
* 3. If not found in any lists, then this operation has either
* been executed already and is not a recurring operation, or
* never existed.
*/
if (!rsc) {
return -ENODEV;
}
for (gIter = rsc->pending_ops; gIter != NULL; gIter = gIter->next) {
lrmd_cmd_t *cmd = gIter->data;
if (safe_str_eq(cmd->action, action) && cmd->interval == interval) {
cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED;
cmd_finalize(cmd, rsc);
return pcmk_ok;
}
}
if (safe_str_eq(rsc->class, "stonith")) {
/* The service library does not handle stonith operations.
* We have to handle recurring stonith opereations ourselves. */
for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) {
lrmd_cmd_t *cmd = gIter->data;
if (safe_str_eq(cmd->action, action) && cmd->interval == interval) {
cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED;
if (rsc->active != cmd) {
cmd_finalize(cmd, rsc);
}
return pcmk_ok;
}
}
} else if (services_action_cancel(rsc_id, normalize_action_name(rsc, action), interval) == TRUE) {
/* The service library will tell the action_complete callback function
* this action was cancelled, which will destroy the cmd and remove
* it from the recurring_op list. Do not do that in this function
* if the service library says it cancelled it. */
return pcmk_ok;
}
return -EOPNOTSUPP;
}
static void
cancel_all_recurring(lrmd_rsc_t *rsc, const char *client_id)
{
GList *cmd_list = NULL;
GList *cmd_iter = NULL;
/* Notice a copy of each list is created when concat is called.
* This prevents odd behavior from occurring when the cmd_list
* is iterated through later on. It is possible the cancel_op
* function may end up modifying the recurring_ops and pending_ops
* lists. If we did not copy those lists, our cmd_list iteration
* could get messed up.*/
if (rsc->recurring_ops) {
cmd_list = g_list_concat(cmd_list, g_list_copy(rsc->recurring_ops));
}
if (rsc->pending_ops) {
cmd_list = g_list_concat(cmd_list, g_list_copy(rsc->pending_ops));
}
if (!cmd_list) {
return;
}
for (cmd_iter = cmd_list; cmd_iter; cmd_iter = cmd_iter->next) {
lrmd_cmd_t *cmd = cmd_iter->data;
if (cmd->interval == 0) {
continue;
}
if (client_id && safe_str_neq(cmd->client_id, client_id)) {
continue;
}
cancel_op(rsc->rsc_id, cmd->action, cmd->interval);
}
/* frees only the copied list data, not the cmds */
g_list_free(cmd_list);
}
static int
process_lrmd_rsc_cancel(lrmd_client_t * client, uint32_t id, xmlNode * request)
{
xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
const char *action = crm_element_value(rsc_xml, F_LRMD_RSC_ACTION);
int interval = 0;
crm_element_value_int(rsc_xml, F_LRMD_RSC_INTERVAL, &interval);
if (!rsc_id || !action) {
return -EINVAL;
}
return cancel_op(rsc_id, action, interval);
}
void
process_lrmd_message(lrmd_client_t * client, uint32_t id, xmlNode * request)
{
int rc = pcmk_ok;
int call_id = 0;
const char *op = crm_element_value(request, F_LRMD_OPERATION);
int do_reply = 0;
int do_notify = 0;
int exit = 0;
crm_trace("Processing %s operation from %s", op, client->id);
crm_element_value_int(request, F_LRMD_CALLID, &call_id);
if (crm_str_eq(op, CRM_OP_REGISTER, TRUE)) {
rc = process_lrmd_signon(client, id, request);
} else if (crm_str_eq(op, LRMD_OP_RSC_REG, TRUE)) {
rc = process_lrmd_rsc_register(client, id, request);
do_notify = 1;
do_reply = 1;
} else if (crm_str_eq(op, LRMD_OP_RSC_INFO, TRUE)) {
process_lrmd_get_rsc_info(client, id, request);
} else if (crm_str_eq(op, LRMD_OP_RSC_UNREG, TRUE)) {
rc = process_lrmd_rsc_unregister(client, id, request);
/* don't notify anyone about failed un-registers */
if (rc == pcmk_ok || rc == -EINPROGRESS) {
do_notify = 1;
}
do_reply = 1;
} else if (crm_str_eq(op, LRMD_OP_RSC_EXEC, TRUE)) {
rc = process_lrmd_rsc_exec(client, id, request);
do_reply = 1;
} else if (crm_str_eq(op, LRMD_OP_RSC_CANCEL, TRUE)) {
rc = process_lrmd_rsc_cancel(client, id, request);
do_reply = 1;
- } else if (crm_str_eq(op, CRM_OP_QUIT, TRUE)) {
- do_reply = 1;
- exit = 1;
} else {
rc = -EOPNOTSUPP;
do_reply = 1;
crm_err("Unknown %s from %s", op, client->name);
crm_log_xml_warn(request, "UnknownOp");
}
crm_debug("Processed %s operation from %s: rc=%d, reply=%d, notify=%d, exit=%d",
op, client->id, rc, do_reply, do_notify, exit);
if (do_reply) {
send_reply(client, rc, id, call_id);
}
if (do_notify) {
send_generic_notify(rc, request);
}
if (exit) {
lrmd_shutdown(0);
}
}
diff --git a/lrmd/main.c b/lrmd/main.c
index 5f5033c85b..954177bb6b 100644
--- a/lrmd/main.c
+++ b/lrmd/main.c
@@ -1,322 +1,280 @@
/*
* Copyright (c) 2012 David Vossel <dvossel@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <crm_internal.h>
#include <glib.h>
#include <unistd.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/common/ipc.h>
#include <lrmd_private.h>
GMainLoop *mainloop = NULL;
qb_ipcs_service_t *ipcs = NULL;
stonith_t *stonith_api = NULL;
static int global_call_id = 0;
static void
stonith_connection_destroy_cb(stonith_t * st, stonith_event_t *e)
{
stonith_api->state = stonith_disconnected;
crm_err("LRMD lost STONITH connection");
stonith_connection_failed();
}
stonith_t *
get_stonith_connection(void)
{
if (stonith_api && stonith_api->state == stonith_disconnected) {
stonith_api_delete(stonith_api);
stonith_api = NULL;
}
if (!stonith_api) {
int rc = 0;
int tries = 10;
stonith_api = stonith_api_new();
do {
rc = stonith_api->cmds->connect(stonith_api, "lrmd", NULL);
if (rc == pcmk_ok) {
stonith_api->cmds->register_notification(stonith_api,
T_STONITH_NOTIFY_DISCONNECT,
stonith_connection_destroy_cb);
break;
}
sleep(1);
tries--;
} while (tries);
if (rc) {
crm_err("Unable to connect to stonith daemon to execute command. error: %s",
pcmk_strerror(rc));
stonith_api_delete(stonith_api);
stonith_api = NULL;
}
}
return stonith_api;
}
static int32_t
lrmd_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
{
struct qb_ipcs_connection_stats stats = { 0, };
qb_ipcs_connection_stats_get(c, &stats, 1);
crm_info("Accepting client connection: %p pid=%d for uid=%d gid=%d",
c, stats.client_pid, uid, gid);
return 0;
}
static void
lrmd_ipc_created(qb_ipcs_connection_t * c)
{
lrmd_client_t *new_client = NULL;
new_client = calloc(1, sizeof(lrmd_client_t));
new_client->channel = c;
new_client->id = crm_generate_uuid();
crm_trace("LRMD client connection established. %p id: %s", c, new_client->id);
g_hash_table_insert(client_list, new_client->id, new_client);
qb_ipcs_context_set(c, new_client);
}
static int32_t
lrmd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
{
uint32_t id = 0;
uint32_t flags = 0;
xmlNode *request = crm_ipcs_recv(c, data, size, &id, &flags);
lrmd_client_t *client = (lrmd_client_t *) qb_ipcs_context_get(c);
CRM_CHECK(client != NULL, crm_err("Invalid client");
return FALSE);
CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p", client);
return FALSE);
CRM_CHECK(flags & crm_ipc_client_response, crm_err("Invalid client request: %p", client);
return FALSE);
if (!request) {
return 0;
}
if (!client->name) {
const char *value = crm_element_value(request, F_LRMD_CLIENTNAME);
if (value == NULL) {
client->name = crm_itoa(crm_ipcs_client_pid(c));
} else {
client->name = strdup(value);
}
}
global_call_id++;
if (global_call_id < 1) {
global_call_id = 1;
}
crm_xml_add(request, F_LRMD_CLIENTID, client->id);
crm_xml_add(request, F_LRMD_CLIENTNAME, client->name);
crm_xml_add_int(request, F_LRMD_CALLID, global_call_id);
process_lrmd_message(client, id, request);
free_xml(request);
return 0;
}
static int32_t
lrmd_ipc_closed(qb_ipcs_connection_t * c)
{
lrmd_client_t *client = (lrmd_client_t *) qb_ipcs_context_get(c);
int found = 0;
if (!client) {
crm_err("No client for ipc");
return 0;
}
if (client->id) {
found = g_hash_table_remove(client_list, client->id);
}
if (!found) {
crm_err("Asked to remove unknown client with id %d", client->id);
}
return 0;
}
static void
lrmd_ipc_destroy(qb_ipcs_connection_t * c)
{
lrmd_client_t *client = (lrmd_client_t *) qb_ipcs_context_get(c);
if (!client) {
crm_err("No client for ipc");
return;
}
crm_info("LRMD client disconnecting %p - name: %s id: %s", c, client->name, client->id);
client_disconnect_cleanup(client->id);
qb_ipcs_context_set(c, NULL);
free(client->name);
free(client->id);
free(client);
}
static struct qb_ipcs_service_handlers lrmd_ipc_callbacks = {
.connection_accept = lrmd_ipc_accept,
.connection_created = lrmd_ipc_created,
.msg_process = lrmd_ipc_dispatch,
.connection_closed = lrmd_ipc_closed,
.connection_destroyed = lrmd_ipc_destroy
};
void
lrmd_shutdown(int nsig)
{
crm_info("Terminating with %d clients", g_hash_table_size(client_list));
if (ipcs) {
mainloop_del_ipc_server(ipcs);
}
crm_exit(0);
}
-static int
-try_server_create(void)
-{
- int tries = 10;
-
- /*
- * This should complete on the first iteration. The only
- * known reason for why this would fail is if another lrmd process
- * already exists on the system. To avoid this situation
- * we attempt to connect to the old lrmd process and shut it down
- * using the client library.
- */
- do {
- ipcs = mainloop_add_ipc_server(CRM_SYSTEM_LRMD, QB_IPC_SHM, &lrmd_ipc_callbacks);
-
- if (ipcs == NULL) {
- xmlNode *reply = NULL;
- xmlNode *hello = create_xml_node(NULL, "lrmd_command");
- crm_ipc_t *ipc = crm_ipc_new("lrmd", 0);
-
- if (ipc && crm_ipc_connect(ipc)) {
- crm_xml_add(hello, F_TYPE, T_LRMD);
- crm_xml_add(hello, F_LRMD_OPERATION, CRM_OP_QUIT);
- crm_xml_add(hello, F_LRMD_CLIENTNAME, "new_lrmd");
-
- crm_ipc_send(ipc, hello, crm_ipc_client_response, 500, &reply);
-
- crm_ipc_close(ipc);
- crm_ipc_destroy(ipc);
- }
- free_xml(reply);
- free_xml(hello);
-
- crm_err
- ("New IPC server could not be created because another lrmd process exists, sending shutdown command to old lrmd process.");
- }
-
- tries--;
- } while (!ipcs && tries);
-
- return ipcs ? 0 : -1;
-}
-
/* *INDENT-OFF* */
static struct crm_option long_options[] = {
/* Top-level Options */
{"help", 0, 0, '?', "\tThis text"},
{"version", 0, 0, '$', "\tVersion information" },
{"verbose", 0, 0, 'V', "\tIncrease debug output"},
{"logfile", 1, 0, 'l', "\tSend logs to the additional named logfile"},
{0, 0, 0, 0}
};
/* *INDENT-ON* */
int
main(int argc, char **argv)
{
int rc = 0;
int flag = 0;
int index = 0;
crm_log_init("lrmd", LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
crm_set_options(NULL, "[options]", long_options, "Daemon for controlling services confirming to different standards");
while (1) {
flag = crm_get_option(argc, argv, &index);
if (flag == -1) {
break;
}
switch (flag) {
case 'l':
crm_add_logfile(optarg);
break;
case 'V':
set_crm_log_level(crm_log_level+1);
break;
case '?':
case '$':
crm_help(flag, EX_OK);
break;
default:
crm_help('?', EX_USAGE);
break;
}
}
rsc_list = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_rsc);
client_list = g_hash_table_new(crm_str_hash, g_str_equal);
- if (try_server_create()) {
- crm_err("Failed to allocate lrmd server. shutting down");
- crm_exit(-1);
+ ipcs = mainloop_add_ipc_server(CRM_SYSTEM_LRMD, QB_IPC_SHM, &lrmd_ipc_callbacks);
+ if (ipcs == NULL) {
+ crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
+ crm_exit(100);
}
mainloop_add_signal(SIGTERM, lrmd_shutdown);
mainloop = g_main_new(FALSE);
crm_info("Starting");
g_main_run(mainloop);
mainloop_del_ipc_server(ipcs);
g_hash_table_destroy(client_list);
g_hash_table_destroy(rsc_list);
if (stonith_api) {
stonith_api->cmds->disconnect(stonith_api);
stonith_api_delete(stonith_api);
}
return rc;
}
diff --git a/pengine/main.c b/pengine/main.c
index 410c56d77b..36a9d92eac 100644
--- a/pengine/main.c
+++ b/pengine/main.c
@@ -1,206 +1,188 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/common/ipc.h>
#include <crm/common/mainloop.h>
#include <crm/pengine/internal.h>
#include <crm/msg_xml.h>
#if HAVE_LIBXML2
# include <libxml/parser.h>
#endif
#define OPTARGS "hVc"
GMainLoop *mainloop = NULL;
qb_ipcs_service_t *ipcs = NULL;
void usage(const char *cmd, int exit_status);
void pengine_shutdown(int nsig);
static int32_t
pe_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
{
crm_trace("Connecting %p for uid=%d gid=%d", c, uid, gid);
return 0;
}
static void
pe_ipc_created(qb_ipcs_connection_t *c)
{
}
gboolean process_pe_message(xmlNode * msg, xmlNode * xml_data, qb_ipcs_connection_t* sender);
static int32_t
pe_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
{
uint32_t id = 0;
uint32_t flags = 0;
xmlNode *msg = crm_ipcs_recv(c, data, size, &id, &flags);
if(flags & crm_ipc_client_response) {
crm_ipcs_send_ack(c, id, "ack", __FUNCTION__, __LINE__);
}
if (msg != NULL) {
xmlNode *data = get_message_xml(msg, F_CRM_DATA);
process_pe_message(msg, data, c);
free_xml(msg);
}
return 0;
}
/* Error code means? */
static int32_t
pe_ipc_closed(qb_ipcs_connection_t *c)
{
return 0;
}
static void
pe_ipc_destroy(qb_ipcs_connection_t *c)
{
crm_trace("Disconnecting %p", c);
}
struct qb_ipcs_service_handlers ipc_callbacks =
{
.connection_accept = pe_ipc_accept,
.connection_created = pe_ipc_created,
.msg_process = pe_ipc_dispatch,
.connection_closed = pe_ipc_closed,
.connection_destroyed = pe_ipc_destroy
};
int
main(int argc, char **argv)
{
int flag;
int argerr = 0;
- crm_ipc_t *old_instance = NULL;
crm_system_name = CRM_SYSTEM_PENGINE;
mainloop_add_signal(SIGTERM, pengine_shutdown);
while ((flag = getopt(argc, argv, OPTARGS)) != EOF) {
switch (flag) {
case 'V':
crm_bump_log_level(argc, argv);
break;
case 'h': /* Help message */
usage(crm_system_name, EX_OK);
break;
default:
++argerr;
break;
}
}
if (argc - optind == 1 && safe_str_eq("metadata", argv[optind])) {
pe_metadata();
return 0;
}
if (optind > argc) {
++argerr;
}
if (argerr) {
usage(crm_system_name, EX_USAGE);
}
crm_log_init(NULL, LOG_NOTICE, TRUE, FALSE, argc, argv, FALSE);
if (crm_is_writable(PE_STATE_DIR, NULL, CRM_DAEMON_USER, CRM_DAEMON_GROUP, FALSE) == FALSE) {
crm_err("Bad permissions on " PE_STATE_DIR ". Terminating");
fprintf(stderr, "ERROR: Bad permissions on " PE_STATE_DIR ". See logs for details\n");
fflush(stderr);
return 100;
}
- /* find any previous instances and shut them down */
- crm_debug("Checking for old instances of %s", CRM_SYSTEM_PENGINE);
- old_instance = crm_ipc_new(CRM_SYSTEM_PENGINE, 0);
- crm_ipc_connect(old_instance);
-
- crm_debug("Terminating previous instance");
- while (crm_ipc_connected(old_instance)) {
- xmlNode *cmd = create_request(CRM_OP_QUIT, NULL, NULL, CRM_SYSTEM_PENGINE, CRM_SYSTEM_PENGINE, NULL);
- crm_debug(".");
- crm_ipc_send(old_instance, cmd, 0, 0, NULL);
- free_xml(cmd);
-
- sleep(2);
- }
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
-
crm_debug("Init server comms");
ipcs = mainloop_add_ipc_server(CRM_SYSTEM_PENGINE, QB_IPC_SHM, &ipc_callbacks);
if (ipcs == NULL) {
- crm_err("Couldn't start IPC server");
- return 1;
+ crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
+ crm_exit(100);
}
/* Create the mainloop and run it... */
crm_info("Starting %s", crm_system_name);
mainloop = g_main_new(FALSE);
g_main_run(mainloop);
crm_info("Exiting %s", crm_system_name);
return crm_exit(0);
}
void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-srkh]" "[-c configure file]\n", cmd);
/* fprintf(stream, "\t-d\tsets debug level\n"); */
/* fprintf(stream, "\t-s\tgets daemon status\n"); */
/* fprintf(stream, "\t-r\trestarts daemon\n"); */
/* fprintf(stream, "\t-k\tstops daemon\n"); */
/* fprintf(stream, "\t-h\thelp message\n"); */
fflush(stream);
crm_exit(exit_status);
}
void
pengine_shutdown(int nsig)
{
mainloop_del_ipc_server(ipcs);
crm_exit(EX_OK);
}
diff --git a/pengine/pengine.c b/pengine/pengine.c
index 2923ea247d..84802f65cc 100644
--- a/pengine/pengine.c
+++ b/pengine/pengine.c
@@ -1,278 +1,274 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <glib.h>
#include <crm/pengine/status.h>
#include <pengine.h>
#include <allocate.h>
#include <utils.h>
xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now);
gboolean show_scores = FALSE;
int scores_log_level = LOG_DEBUG_2;
gboolean show_utilization = FALSE;
int utilization_log_level = LOG_DEBUG_2;
extern int transition_id;
#define get_series() was_processing_error?1:was_processing_warning?2:3
typedef struct series_s {
const char *name;
const char *param;
int wrap;
} series_t;
series_t series[] = {
{"pe-unknown", "_dont_match_anything_", -1},
{"pe-error", "pe-error-series-max", -1},
{"pe-warn", "pe-warn-series-max", 200},
{"pe-input", "pe-input-series-max", 400},
};
gboolean process_pe_message(xmlNode * msg, xmlNode * xml_data, qb_ipcs_connection_t* sender);
gboolean
process_pe_message(xmlNode * msg, xmlNode * xml_data, qb_ipcs_connection_t* sender)
{
static char *last_digest = NULL;
const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO);
const char *op = crm_element_value(msg, F_CRM_TASK);
const char *ref = crm_element_value(msg, XML_ATTR_REFERENCE);
crm_trace("Processing %s op (ref=%s)...", op, ref);
if (op == NULL) {
/* error */
} else if (strcasecmp(op, CRM_OP_HELLO) == 0) {
/* ignore */
} else if (safe_str_eq(crm_element_value(msg, F_CRM_MSG_TYPE), XML_ATTR_RESPONSE)) {
/* ignore */
} else if (sys_to == NULL || strcasecmp(sys_to, CRM_SYSTEM_PENGINE) != 0) {
crm_trace("Bad sys-to %s", crm_str(sys_to));
return FALSE;
} else if (strcasecmp(op, CRM_OP_PECALC) == 0) {
int seq = -1;
int series_id = 0;
int series_wrap = 0;
char *digest = NULL;
const char *value = NULL;
pe_working_set_t data_set;
xmlNode *converted = NULL;
xmlNode *reply = NULL;
gboolean is_repoke = FALSE;
gboolean process = TRUE;
char *filename = NULL;
crm_config_error = FALSE;
crm_config_warning = FALSE;
was_processing_error = FALSE;
was_processing_warning = FALSE;
set_working_set_defaults(&data_set);
digest = calculate_xml_versioned_digest(xml_data, FALSE, FALSE, CRM_FEATURE_SET);
converted = copy_xml(xml_data);
if (cli_config_update(&converted, NULL, TRUE) == FALSE) {
data_set.graph = create_xml_node(NULL, XML_TAG_GRAPH);
crm_xml_add_int(data_set.graph, "transition_id", 0);
crm_xml_add_int(data_set.graph, "cluster-delay", 0);
process = FALSE;
free(digest);
} else if (safe_str_eq(digest, last_digest)) {
crm_info("Input has not changed since last time, not saving to disk");
is_repoke = TRUE;
free(digest);
} else {
free(last_digest);
last_digest = digest;
}
if (process) {
do_calculations(&data_set, converted, NULL);
}
series_id = get_series();
series_wrap = series[series_id].wrap;
value = pe_pref(data_set.config_hash, series[series_id].param);
if (value != NULL) {
series_wrap = crm_int_helper(value, NULL);
if (errno != 0) {
series_wrap = series[series_id].wrap;
}
} else {
crm_config_warn("No value specified for cluster"
" preference: %s", series[series_id].param);
}
seq = get_last_sequence(PE_STATE_DIR, series[series_id].name);
crm_trace("Series %s: wrap=%d, seq=%d, pref=%s",
series[series_id].name, series_wrap, seq, value);
data_set.input = NULL;
reply = create_reply(msg, data_set.graph);
CRM_ASSERT(reply != NULL);
if (is_repoke == FALSE) {
filename = generate_series_filename(PE_STATE_DIR, series[series_id].name, seq, HAVE_BZLIB_H);
}
crm_xml_add(reply, F_CRM_TGRAPH_INPUT, filename);
crm_xml_add_int(reply, "graph-errors", was_processing_error);
crm_xml_add_int(reply, "graph-warnings", was_processing_warning);
crm_xml_add_int(reply, "config-errors", crm_config_error);
crm_xml_add_int(reply, "config-warnings", crm_config_warning);
if (crm_ipcs_send(sender, 0, reply, TRUE) == FALSE) {
crm_err("Couldn't send transition graph to peer, discarding");
}
free_xml(reply);
cleanup_alloc_calculations(&data_set);
if (was_processing_error) {
crm_err("Calculated Transition %d: %s",
transition_id, filename);
} else if (was_processing_warning) {
crm_warn("Calculated Transition %d: %s",
transition_id, filename);
} else {
crm_notice("Calculated Transition %d: %s",
transition_id, filename);
}
if (crm_config_error) {
crm_notice("Configuration ERRORs found during PE processing."
" Please run \"crm_verify -L\" to identify issues.");
}
if (is_repoke == FALSE && series_wrap != 0) {
write_xml_file(xml_data, filename, HAVE_BZLIB_H);
write_last_sequence(PE_STATE_DIR, series[series_id].name,
seq + 1, series_wrap);
} else {
crm_trace("Not writing out %s: %d & %d",
filename, is_repoke, series_wrap);
}
free_xml(converted);
free(filename);
-
- } else if (strcasecmp(op, CRM_OP_QUIT) == 0) {
- crm_warn("Received quit message, terminating");
- crm_exit(0);
}
return TRUE;
}
xmlNode *
do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now)
{
GListPtr gIter = NULL;
int rsc_log_level = LOG_INFO;
/* pe_debug_on(); */
CRM_ASSERT(xml_input || is_set(data_set->flags, pe_flag_have_status));
if (is_set(data_set->flags, pe_flag_have_status) == FALSE) {
set_working_set_defaults(data_set);
data_set->input = xml_input;
data_set->now = now;
if (data_set->now == NULL) {
data_set->now = crm_time_new(NULL);
}
} else {
crm_trace("Already have status - reusing");
}
crm_trace("Calculate cluster status");
stage0(data_set);
gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
if (is_set(rsc->flags, pe_rsc_orphan) && rsc->role == RSC_ROLE_STOPPED) {
continue;
}
rsc->fns->print(rsc, NULL, pe_print_log, &rsc_log_level);
}
crm_trace("Applying placement constraints");
stage2(data_set);
crm_trace("Create internal constraints");
stage3(data_set);
crm_trace("Check actions");
stage4(data_set);
crm_trace("Allocate resources");
stage5(data_set);
crm_trace("Processing fencing and shutdown cases");
stage6(data_set);
crm_trace("Applying ordering constraints");
stage7(data_set);
crm_trace("Create transition graph");
stage8(data_set);
crm_trace("=#=#=#=#= Summary =#=#=#=#=");
crm_trace("\t========= Set %d (Un-runnable) =========", -1);
if (get_crm_log_level() >= LOG_TRACE) {
gIter = data_set->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
if (is_set(action->flags, pe_action_optional) == FALSE
&& is_set(action->flags, pe_action_runnable) == FALSE
&& is_set(action->flags, pe_action_pseudo) == FALSE) {
log_action(LOG_TRACE, "\t", action, TRUE);
}
}
}
return data_set->graph;
}
diff --git a/tools/attrd.c b/tools/attrd.c
index eeaf1ccaa7..1623d231c7 100644
--- a/tools/attrd.c
+++ b/tools/attrd.c
@@ -1,916 +1,916 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/common/mainloop.h>
#include <crm/common/xml.h>
#include <crm/attrd.h>
#define OPTARGS "hV"
#if SUPPORT_HEARTBEAT
ll_cluster_t *attrd_cluster_conn;
#endif
GMainLoop *mainloop = NULL;
char *attrd_uname = NULL;
char *attrd_uuid = NULL;
gboolean need_shutdown = FALSE;
GHashTable *attr_hash = NULL;
cib_t *cib_conn = NULL;
typedef struct attrd_client_s {
char *user;
} attrd_client_t;
typedef struct attr_hash_entry_s {
char *uuid;
char *id;
char *set;
char *section;
char *value;
char *stored_value;
int timeout;
char *dampen;
guint timer_id;
char *user;
} attr_hash_entry_t;
void attrd_local_callback(xmlNode * msg);
gboolean attrd_timer_callback(void *user_data);
gboolean attrd_trigger_update(attr_hash_entry_t * hash_entry);
void attrd_perform_update(attr_hash_entry_t * hash_entry);
static void
free_hash_entry(gpointer data)
{
attr_hash_entry_t *entry = data;
if (entry == NULL) {
return;
}
free(entry->id);
free(entry->set);
free(entry->dampen);
free(entry->section);
free(entry->uuid);
free(entry->value);
free(entry->stored_value);
free(entry->user);
free(entry);
}
static int32_t
attrd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
{
attrd_client_t *new_client = NULL;
#if ENABLE_ACL
struct group *crm_grp = NULL;
#endif
crm_trace("Connecting %p for connection from %d by uid=%d gid=%d",
c, crm_ipcs_client_pid(c), uid, gid);
if (need_shutdown) {
crm_info("Ignoring connection request during shutdown");
return FALSE;
}
new_client = calloc(1, sizeof(attrd_client_t));
#if ENABLE_ACL
crm_grp = getgrnam(CRM_DAEMON_GROUP);
if (crm_grp) {
qb_ipcs_connection_auth_set(c, -1, crm_grp->gr_gid, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
}
new_client->user = uid2username(uid);
#endif
qb_ipcs_context_set(c, new_client);
return 0;
}
static void
attrd_ipc_created(qb_ipcs_connection_t *c)
{
crm_trace("Client %p connected from %d", c, crm_ipcs_client_pid(c));
}
/* Exit code means? */
static int32_t
attrd_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
{
uint32_t id = 0;
uint32_t flags = 0;
#if ENABLE_ACL
attrd_client_t *client = qb_ipcs_context_get(c);
#endif
xmlNode *msg = crm_ipcs_recv(c, data, size, &id, &flags);
if(flags & crm_ipc_client_response) {
crm_trace("Ack'ing msg from %d (%p)", crm_ipcs_client_pid(c), c);
crm_ipcs_send_ack(c, id, "ack", __FUNCTION__, __LINE__);
}
if (msg == NULL) {
crm_debug("No msg from %d (%p)", crm_ipcs_client_pid(c), c);
return 0;
}
#if ENABLE_ACL
determine_request_user(client->user, msg, F_ATTRD_USER);
#endif
crm_trace("Processing msg from %d (%p)", crm_ipcs_client_pid(c), c);
crm_log_xml_trace(msg, __PRETTY_FUNCTION__);
attrd_local_callback(msg);
free_xml(msg);
return 0;
}
/* Error code means? */
static int32_t
attrd_ipc_closed(qb_ipcs_connection_t *c)
{
crm_trace("Connection %p from %d closed", c, crm_ipcs_client_pid(c));
return 0;
}
static void
attrd_ipc_destroy(qb_ipcs_connection_t *c)
{
attrd_client_t *client = qb_ipcs_context_get(c);
if (client == NULL) {
return;
}
crm_trace("Destroying %p", c);
free(client->user);
free(client);
crm_trace("Free'd the attrd client");
return;
}
struct qb_ipcs_service_handlers ipc_callbacks =
{
.connection_accept = attrd_ipc_accept,
.connection_created = attrd_ipc_created,
.msg_process = attrd_ipc_dispatch,
.connection_closed = attrd_ipc_closed,
.connection_destroyed = attrd_ipc_destroy
};
static void
attrd_shutdown(int nsig)
{
need_shutdown = TRUE;
crm_info("Exiting");
if (mainloop != NULL && g_main_is_running(mainloop)) {
g_main_quit(mainloop);
} else {
crm_exit(0);
}
}
static void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-srkh] [-c configure file]\n", cmd);
/* fprintf(stream, "\t-d\tsets debug level\n"); */
/* fprintf(stream, "\t-s\tgets daemon status\n"); */
/* fprintf(stream, "\t-r\trestarts daemon\n"); */
/* fprintf(stream, "\t-k\tstops daemon\n"); */
/* fprintf(stream, "\t-h\thelp message\n"); */
fflush(stream);
crm_exit(exit_status);
}
static void
stop_attrd_timer(attr_hash_entry_t * hash_entry)
{
if (hash_entry != NULL && hash_entry->timer_id != 0) {
crm_trace("Stopping %s timer", hash_entry->id);
g_source_remove(hash_entry->timer_id);
hash_entry->timer_id = 0;
}
}
static void
log_hash_entry(int level, attr_hash_entry_t * entry, const char *text)
{
do_crm_log(level, "%s: Set: %s, Name: %s, Value: %s, Timeout: %s",
text, entry->section, entry->id, entry->value, entry->dampen);
}
static attr_hash_entry_t *
find_hash_entry(xmlNode * msg)
{
const char *value = NULL;
const char *attr = crm_element_value(msg, F_ATTRD_ATTRIBUTE);
attr_hash_entry_t *hash_entry = NULL;
if (attr == NULL) {
crm_info("Ignoring message with no attribute name");
return NULL;
}
hash_entry = g_hash_table_lookup(attr_hash, attr);
if (hash_entry == NULL) {
/* create one and add it */
crm_info("Creating hash entry for %s", attr);
hash_entry = calloc(1, sizeof(attr_hash_entry_t));
hash_entry->id = strdup(attr);
g_hash_table_insert(attr_hash, hash_entry->id, hash_entry);
hash_entry = g_hash_table_lookup(attr_hash, attr);
CRM_CHECK(hash_entry != NULL, return NULL);
}
value = crm_element_value(msg, F_ATTRD_SET);
if (value != NULL) {
free(hash_entry->set);
hash_entry->set = strdup(value);
crm_debug("\t%s->set: %s", attr, value);
}
value = crm_element_value(msg, F_ATTRD_SECTION);
if (value == NULL) {
value = XML_CIB_TAG_STATUS;
}
free(hash_entry->section);
hash_entry->section = strdup(value);
crm_trace("\t%s->section: %s", attr, value);
value = crm_element_value(msg, F_ATTRD_DAMPEN);
if (value != NULL) {
free(hash_entry->dampen);
hash_entry->dampen = strdup(value);
hash_entry->timeout = crm_get_msec(value);
crm_trace("\t%s->timeout: %s", attr, value);
}
#if ENABLE_ACL
free(hash_entry->user);
value = crm_element_value(msg, F_ATTRD_USER);
if (value != NULL) {
hash_entry->user = strdup(value);
crm_trace("\t%s->user: %s", attr, value);
}
#endif
log_hash_entry(LOG_DEBUG_2, hash_entry, "Found (and updated) entry:");
return hash_entry;
}
#if SUPPORT_HEARTBEAT
static void
attrd_ha_connection_destroy(gpointer user_data)
{
crm_trace("Invoked");
if (need_shutdown) {
/* we signed out, so this is expected */
crm_info("Heartbeat disconnection complete");
return;
}
crm_crit("Lost connection to heartbeat service!");
if (mainloop != NULL && g_main_is_running(mainloop)) {
g_main_quit(mainloop);
return;
}
crm_exit(EX_OK);
}
static void
attrd_ha_callback(HA_Message * msg, void *private_data)
{
attr_hash_entry_t *hash_entry = NULL;
xmlNode *xml = convert_ha_message(NULL, msg, __FUNCTION__);
const char *from = crm_element_value(xml, F_ORIG);
const char *op = crm_element_value(xml, F_ATTRD_TASK);
const char *host = crm_element_value(xml, F_ATTRD_HOST);
const char *ignore = crm_element_value(xml, F_ATTRD_IGNORE_LOCALLY);
if (host != NULL && safe_str_eq(host, attrd_uname)) {
crm_info("Update relayed from %s", from);
attrd_local_callback(xml);
} else if (ignore == NULL || safe_str_neq(from, attrd_uname)) {
crm_info("%s message from %s", op, from);
hash_entry = find_hash_entry(xml);
stop_attrd_timer(hash_entry);
attrd_perform_update(hash_entry);
}
free_xml(xml);
}
#endif
#if SUPPORT_COROSYNC
static gboolean
attrd_ais_dispatch(int kind, const char *from, const char *data)
{
xmlNode *xml = NULL;
if (kind == crm_class_cluster) {
xml = string2xml(data);
if (xml == NULL) {
crm_err("Bad message received: '%.120s'", data);
}
}
if (xml != NULL) {
attr_hash_entry_t *hash_entry = NULL;
const char *op = crm_element_value(xml, F_ATTRD_TASK);
const char *host = crm_element_value(xml, F_ATTRD_HOST);
const char *ignore = crm_element_value(xml, F_ATTRD_IGNORE_LOCALLY);
/* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
crm_xml_add(xml, F_ORIG, from);
if (host != NULL && safe_str_eq(host, attrd_uname)) {
crm_notice("Update relayed from %s", from);
attrd_local_callback(xml);
} else if (ignore == NULL || safe_str_neq(from, attrd_uname)) {
crm_trace("%s message from %s", op, from);
hash_entry = find_hash_entry(xml);
stop_attrd_timer(hash_entry);
attrd_perform_update(hash_entry);
}
free_xml(xml);
}
return TRUE;
}
static void
attrd_ais_destroy(gpointer unused)
{
if (need_shutdown) {
/* we signed out, so this is expected */
crm_info("Corosync disconnection complete");
return;
}
crm_crit("Lost connection to Corosync service!");
if (mainloop != NULL && g_main_is_running(mainloop)) {
g_main_quit(mainloop);
return;
}
crm_exit(EX_USAGE);
}
#endif
static void
attrd_cib_connection_destroy(gpointer user_data)
{
cib_t *conn = user_data;
conn->cmds->signoff(conn); /* Ensure IPC is cleaned up */
if (need_shutdown) {
crm_info("Connection to the CIB terminated...");
} else {
/* eventually this will trigger a reconnect, not a shutdown */
crm_err("Connection to the CIB terminated...");
crm_exit(1);
}
return;
}
static void
update_for_hash_entry(gpointer key, gpointer value, gpointer user_data)
{
attr_hash_entry_t *entry = value;
if (entry->value != NULL) {
attrd_timer_callback(value);
}
}
static void
local_update_for_hash_entry(gpointer key, gpointer value, gpointer user_data)
{
attr_hash_entry_t *entry = value;
if (entry->timer_id == 0) {
crm_trace("Performing local-only update after replace for %s", entry->id);
attrd_perform_update(entry);
/* } else {
* just let the timer expire and attrd_timer_callback() will do the right thing
*/
}
}
static void
do_cib_replaced(const char *event, xmlNode * msg)
{
crm_info("Updating all attributes after %s event", event);
g_hash_table_foreach(attr_hash, local_update_for_hash_entry, NULL);
}
static gboolean
cib_connect(void *user_data)
{
static int attempts = 1;
static int max_retry = 20;
gboolean was_err = FALSE;
static cib_t *local_conn = NULL;
if (local_conn == NULL) {
local_conn = cib_new();
}
if (was_err == FALSE) {
int rc = -ENOTCONN;
if (attempts < max_retry) {
crm_debug("CIB signon attempt %d", attempts);
rc = local_conn->cmds->signon(local_conn, T_ATTRD, cib_command);
}
if (rc != pcmk_ok && attempts > max_retry) {
crm_err("Signon to CIB failed: %s", pcmk_strerror(rc));
was_err = TRUE;
} else if (rc != pcmk_ok) {
attempts++;
return TRUE;
}
}
crm_info("Connected to the CIB after %d signon attempts", attempts);
if (was_err == FALSE) {
int rc =
local_conn->cmds->set_connection_dnotify(local_conn, attrd_cib_connection_destroy);
if (rc != pcmk_ok) {
crm_err("Could not set dnotify callback");
was_err = TRUE;
}
}
if (was_err == FALSE) {
if (pcmk_ok !=
local_conn->cmds->add_notify_callback(local_conn, T_CIB_REPLACE_NOTIFY,
do_cib_replaced)) {
crm_err("Could not set CIB notification callback");
was_err = TRUE;
}
}
if (was_err) {
crm_err("Aborting startup");
crm_exit(100);
}
cib_conn = local_conn;
crm_info("Sending full refresh now that we're connected to the cib");
g_hash_table_foreach(attr_hash, local_update_for_hash_entry, NULL);
return FALSE;
}
int
main(int argc, char **argv)
{
int flag = 0;
int argerr = 0;
crm_cluster_t cluster;
gboolean was_err = FALSE;
qb_ipcs_connection_t *c = NULL;
qb_ipcs_service_t *ipcs = NULL;
crm_log_init(T_ATTRD, LOG_NOTICE, TRUE, FALSE, argc, argv, FALSE);
mainloop_add_signal(SIGTERM, attrd_shutdown);
while ((flag = getopt(argc, argv, OPTARGS)) != EOF) {
switch (flag) {
case 'V':
crm_bump_log_level(argc, argv);
break;
case 'h': /* Help message */
usage(T_ATTRD, EX_OK);
break;
default:
++argerr;
break;
}
}
if (optind > argc) {
++argerr;
}
if (argerr) {
usage(T_ATTRD, EX_USAGE);
}
attr_hash = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_hash_entry);
crm_info("Starting up");
if (was_err == FALSE) {
#if SUPPORT_COROSYNC
if (is_openais_cluster()) {
cluster.destroy = attrd_ais_destroy;
cluster.cs_dispatch = attrd_ais_dispatch;
}
#endif
#if SUPPORT_HEARTBEAT
if (is_heartbeat_cluster()) {
cluster.hb_conn = NULL;
cluster.hb_dispatch = attrd_ha_callback;
cluster.destroy = attrd_ha_connection_destroy;
}
#endif
if (FALSE == crm_cluster_connect(&cluster)) {
crm_err("HA Signon failed");
was_err = TRUE;
}
attrd_uname = cluster.uname;
attrd_uuid = cluster.uuid;
#if SUPPORT_HEARTBEAT
attrd_cluster_conn = cluster.hb_conn;
#endif
}
crm_info("Cluster connection active");
if (was_err == FALSE) {
ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, &ipc_callbacks);
if (ipcs == NULL) {
- crm_err("Could not start IPC server");
- was_err = TRUE;
+ crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
+ crm_exit(100);
}
}
crm_info("Accepting attribute updates");
mainloop = g_main_new(FALSE);
if (0 == g_timeout_add_full(G_PRIORITY_LOW + 1, 5000, cib_connect, NULL, NULL)) {
crm_info("Adding timer failed");
was_err = TRUE;
}
if (was_err) {
crm_err("Aborting startup");
return 100;
}
crm_notice("Starting mainloop...");
g_main_run(mainloop);
crm_notice("Exiting...");
#if SUPPORT_HEARTBEAT
if (is_heartbeat_cluster()) {
attrd_cluster_conn->llc_ops->signoff(attrd_cluster_conn, TRUE);
attrd_cluster_conn->llc_ops->delete(attrd_cluster_conn);
}
#endif
c = qb_ipcs_connection_first_get(ipcs);
while(c != NULL) {
qb_ipcs_connection_t *last = c;
c = qb_ipcs_connection_next_get(ipcs, last);
/* There really shouldn't be anyone connected at this point */
crm_notice("Disconnecting client %p, pid=%d...", last, crm_ipcs_client_pid(last));
qb_ipcs_disconnect(last);
qb_ipcs_connection_unref(last);
}
qb_ipcs_destroy(ipcs);
if (cib_conn) {
cib_conn->cmds->signoff(cib_conn);
cib_delete(cib_conn);
}
g_hash_table_destroy(attr_hash);
free(attrd_uuid);
empty_uuid_cache();
return crm_exit(0);
}
struct attrd_callback_s {
char *attr;
char *value;
};
static void
attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
attr_hash_entry_t *hash_entry = NULL;
struct attrd_callback_s *data = user_data;
if(call_id < 0) {
crm_warn("Update %s=%s failed: %s", data->attr, data->value, pcmk_strerror(call_id));
goto cleanup;
} else if (data->value == NULL && rc == -ENXIO) {
rc = pcmk_ok;
}
switch (rc) {
case pcmk_ok:
crm_debug("Update %d for %s=%s passed", call_id, data->attr, data->value);
hash_entry = g_hash_table_lookup(attr_hash, data->attr);
if (hash_entry) {
free(hash_entry->stored_value);
hash_entry->stored_value = NULL;
if (data->value != NULL) {
hash_entry->stored_value = strdup(data->value);
}
}
break;
case -pcmk_err_diff_failed: /* When an attr changes while the CIB is syncing */
case -ETIME: /* When an attr changes while there is a DC election */
case -ENXIO: /* When an attr changes while the CIB is syncing a
* newer config from a node that just came up
*/
crm_warn("Update %d for %s=%s failed: %s",
call_id, data->attr, data->value, pcmk_strerror(rc));
break;
default:
crm_err("Update %d for %s=%s failed: %s",
call_id, data->attr, data->value, pcmk_strerror(rc));
}
cleanup:
free(data->value);
free(data->attr);
free(data);
}
void
attrd_perform_update(attr_hash_entry_t * hash_entry)
{
int rc = pcmk_ok;
struct attrd_callback_s *data = NULL;
const char *user_name = NULL;
if (hash_entry == NULL) {
return;
} else if (cib_conn == NULL) {
crm_info("Delaying operation %s=%s: cib not connected", hash_entry->id,
crm_str(hash_entry->value));
return;
}
#if ENABLE_ACL
if (hash_entry->user) {
user_name = hash_entry->user;
crm_trace("Performing request from user '%s'", hash_entry->user);
}
#endif
if (hash_entry->value == NULL) {
/* delete the attr */
rc = delete_attr_delegate(cib_conn, cib_none, hash_entry->section, attrd_uuid, NULL,
hash_entry->set, hash_entry->uuid, hash_entry->id, NULL, FALSE,
user_name);
if (rc >= 0 && hash_entry->stored_value) {
crm_notice("Sent delete %d: node=%s, attr=%s, id=%s, set=%s, section=%s",
rc, attrd_uuid, hash_entry->id,
hash_entry->uuid ? hash_entry->uuid : "<n/a>", hash_entry->set,
hash_entry->section);
} else if (rc < 0 && rc != -ENXIO) {
crm_notice
("Delete operation failed: node=%s, attr=%s, id=%s, set=%s, section=%s: %s (%d)",
attrd_uuid, hash_entry->id, hash_entry->uuid ? hash_entry->uuid : "<n/a>",
hash_entry->set, hash_entry->section, pcmk_strerror(rc), rc);
} else {
crm_trace("Sent delete %d: node=%s, attr=%s, id=%s, set=%s, section=%s",
rc, attrd_uuid, hash_entry->id,
hash_entry->uuid ? hash_entry->uuid : "<n/a>", hash_entry->set,
hash_entry->section);
}
} else {
/* send update */
rc = update_attr_delegate(cib_conn, cib_none, hash_entry->section,
attrd_uuid, NULL, hash_entry->set, hash_entry->uuid,
hash_entry->id, hash_entry->value, FALSE, user_name);
if (rc < 0) {
crm_notice("Sent update %s=%s failed: %s", hash_entry->id, hash_entry->value, pcmk_strerror(rc));
} if (safe_str_neq(hash_entry->value, hash_entry->stored_value) || rc < 0) {
crm_notice("Sent update %d: %s=%s", rc, hash_entry->id, hash_entry->value);
} else {
crm_trace("Sent update %d: %s=%s", rc, hash_entry->id, hash_entry->value);
}
}
data = calloc(1, sizeof(struct attrd_callback_s));
data->attr = strdup(hash_entry->id);
if (hash_entry->value != NULL) {
data->value = strdup(hash_entry->value);
}
add_cib_op_callback(cib_conn, rc, FALSE, data, attrd_cib_callback);
return;
}
void
attrd_local_callback(xmlNode * msg)
{
static int plus_plus_len = 5;
attr_hash_entry_t *hash_entry = NULL;
const char *from = crm_element_value(msg, F_ORIG);
const char *op = crm_element_value(msg, F_ATTRD_TASK);
const char *attr = crm_element_value(msg, F_ATTRD_ATTRIBUTE);
const char *value = crm_element_value(msg, F_ATTRD_VALUE);
const char *host = crm_element_value(msg, F_ATTRD_HOST);
if (safe_str_eq(op, "refresh")) {
crm_notice("Sending full refresh (origin=%s)", from);
g_hash_table_foreach(attr_hash, update_for_hash_entry, NULL);
return;
}
if (host != NULL && safe_str_neq(host, attrd_uname)) {
send_cluster_message(crm_get_peer(0, host), crm_msg_attrd, msg, FALSE);
return;
}
crm_debug("%s message from %s: %s=%s", op, from, attr, crm_str(value));
hash_entry = find_hash_entry(msg);
if (hash_entry == NULL) {
return;
}
if (hash_entry->uuid == NULL) {
const char *key = crm_element_value(msg, F_ATTRD_KEY);
if (key) {
hash_entry->uuid = strdup(key);
}
}
crm_debug("Supplied: %s, Current: %s, Stored: %s",
value, hash_entry->value, hash_entry->stored_value);
if (safe_str_eq(value, hash_entry->value)
&& safe_str_eq(value, hash_entry->stored_value)) {
crm_trace("Ignoring non-change");
return;
} else if (value) {
int offset = 1;
int int_value = 0;
int value_len = strlen(value);
if (value_len < (plus_plus_len + 2)
|| value[plus_plus_len] != '+'
|| (value[plus_plus_len + 1] != '+' && value[plus_plus_len + 1] != '=')) {
goto set_unexpanded;
}
int_value = char2score(hash_entry->value);
if (value[plus_plus_len + 1] != '+') {
const char *offset_s = value + (plus_plus_len + 2);
offset = char2score(offset_s);
}
int_value += offset;
if (int_value > INFINITY) {
int_value = INFINITY;
}
crm_info("Expanded %s=%s to %d", attr, value, int_value);
crm_xml_add_int(msg, F_ATTRD_VALUE, int_value);
value = crm_element_value(msg, F_ATTRD_VALUE);
}
set_unexpanded:
if (safe_str_eq(value, hash_entry->value) && hash_entry->timer_id) {
/* We're already waiting to set this value */
return;
}
free(hash_entry->value);
hash_entry->value = NULL;
if (value != NULL) {
hash_entry->value = strdup(value);
crm_debug("New value of %s is %s", attr, value);
}
stop_attrd_timer(hash_entry);
if (hash_entry->timeout > 0) {
hash_entry->timer_id = g_timeout_add(hash_entry->timeout, attrd_timer_callback, hash_entry);
} else {
attrd_trigger_update(hash_entry);
}
return;
}
gboolean
attrd_timer_callback(void *user_data)
{
stop_attrd_timer(user_data);
attrd_trigger_update(user_data);
return TRUE; /* Always return true, removed cleanly by stop_attrd_timer() */
}
gboolean
attrd_trigger_update(attr_hash_entry_t * hash_entry)
{
xmlNode *msg = NULL;
/* send HA message to everyone */
crm_notice("Sending flush op to all hosts for: %s (%s)",
hash_entry->id, crm_str(hash_entry->value));
log_hash_entry(LOG_DEBUG_2, hash_entry, "Sending flush op to all hosts for:");
msg = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(msg, F_TYPE, T_ATTRD);
crm_xml_add(msg, F_ORIG, attrd_uname);
crm_xml_add(msg, F_ATTRD_TASK, "flush");
crm_xml_add(msg, F_ATTRD_ATTRIBUTE, hash_entry->id);
crm_xml_add(msg, F_ATTRD_SET, hash_entry->set);
crm_xml_add(msg, F_ATTRD_SECTION, hash_entry->section);
crm_xml_add(msg, F_ATTRD_DAMPEN, hash_entry->dampen);
crm_xml_add(msg, F_ATTRD_VALUE, hash_entry->value);
#if ENABLE_ACL
if (hash_entry->user) {
crm_xml_add(msg, F_ATTRD_USER, hash_entry->user);
}
#endif
if (hash_entry->timeout <= 0) {
crm_xml_add(msg, F_ATTRD_IGNORE_LOCALLY, hash_entry->value);
attrd_perform_update(hash_entry);
}
send_cluster_message(NULL, crm_msg_attrd, msg, FALSE);
free_xml(msg);
return TRUE;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jun 26, 7:53 PM (1 d, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1959618
Default Alt Text
(195 KB)

Event Timeline