diff --git a/attrd/commands.c b/attrd/commands.c index f5eacda75b..ae01dcca80 100644 --- a/attrd/commands.c +++ b/attrd/commands.c @@ -1,395 +1,506 @@ /* * Copyright (C) 2013 Andrew Beekhof * * 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 #include #include #include #include #include +#include #include #include cib_t *the_cib = NULL; GHashTable *attributes = NULL; typedef struct attribute_s { char *uuid; /* TODO: Remove if at all possible */ char *id; char *set; GHashTable *values; int timeout; bool changed; bool updating; mainloop_timer_t *timer; char *user; } attribute_t; typedef struct attribute_value_s { char *current; char *requested; char *stored; } attribute_value_t; void write_attribute(attribute_t *a); +void write_attributes(bool all); +void attrd_peer_update(crm_node_t *peer, xmlNode *xml); +void attrd_peer_sync(crm_node_t *peer, xmlNode *xml); bool build_update_element(xmlNode *parent, attribute_t *a, const char *node_uuid, const char *attr_value); static gboolean attribute_timer_cb(gpointer user_data) { - write_attribute(user_data); + if(election_state(writer) == election_won) { + write_attribute(user_data); + } return FALSE; } static void free_attribute_value(gpointer data) { attribute_value_t *v = data; free(v->current); free(v->requested); free(v->stored); free(v); } void free_attribute(gpointer data) { attribute_t *a = data; if(a) { free(a->id); free(a->set); free(a->uuid); free(a->user); mainloop_timer_del(a->timer); g_hash_table_destroy(a->values); free(a); } } +xmlNode * +build_attribute_xml( + xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout, const char *user, + const char *peer, const char *value) +{ + xmlNode *xml = create_xml_node(parent, __FUNCTION__); + + crm_xml_add(xml, F_ATTRD_ATTRIBUTE, name); + crm_xml_add(xml, F_ATTRD_SET, set); + crm_xml_add(xml, F_ATTRD_KEY, uuid); + crm_xml_add(xml, F_ATTRD_USER, user); + crm_xml_add(xml, F_ATTRD_HOST, peer); + crm_xml_add(xml, F_ATTRD_VALUE, value); + crm_xml_add_int(xml, F_ATTRD_DAMPEN, timeout); + + return xml; +} + static attribute_t * create_attribute(xmlNode *xml) { attribute_t *a = calloc(1, sizeof(attribute_t)); - const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE); a->id = crm_element_value_copy(xml, F_ATTRD_ATTRIBUTE); a->set = crm_element_value_copy(xml, F_ATTRD_SET); a->uuid = crm_element_value_copy(xml, F_ATTRD_KEY); a->values = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, free_attribute_value); #if ENABLE_ACL crm_trace("Performing all %s operations as user '%s'", a->id, a->user); a->user = crm_element_value_copy(xml, F_ATTRD_USER); #endif crm_element_value_int(xml, F_ATTRD_DAMPEN, &a->timeout); if(a->timeout > 0) { - a->timer = mainloop_timer_add(attr, a->timeout, FALSE, attribute_timer_cb, a); + a->timer = mainloop_timer_add(strdup(a->id), a->timeout, FALSE, attribute_timer_cb, a); } - g_hash_table_replace(attributes, strdup(attr), a); + g_hash_table_replace(attributes, a->id, a); return a; } void attrd_client_message(crm_client_t *client, xmlNode *xml) { static int plus_plus_len = 5; const char *op = crm_element_value(xml, F_ATTRD_TASK); if(safe_str_eq(op, "update")) { attribute_t *a = NULL; attribute_value_t *v = NULL; char *key = crm_element_value_copy(xml, F_ATTRD_KEY); char *set = crm_element_value_copy(xml, F_ATTRD_SET); char *host = crm_element_value_copy(xml, F_ATTRD_HOST); const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE); const char *value = crm_element_value(xml, F_ATTRD_VALUE); a = g_hash_table_lookup(attributes, attr); if(host == NULL) { host = cluster->uname; crm_xml_add(xml, F_ATTRD_HOST, host); } if (set == NULL) { if(a == NULL) { set = g_strdup_printf("%s-%s", XML_CIB_TAG_STATUS, host); } else if(set == NULL) { set = a->set; } crm_xml_add(xml, F_ATTRD_SET, a->set); } if (key == NULL) { if(a == NULL) { int lpc = 0; key = g_strdup_printf("%s-%s", set, attr); /* Minimal attempt at sanitizing automatic IDs */ for (lpc = 0; key[lpc] != 0; lpc++) { switch (key[lpc]) { case ':': key[lpc] = '.'; } } } else if(key == NULL) { key = a->uuid; } crm_xml_add(xml, F_ATTRD_KEY, key); } 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 send; } if(a) { v = g_hash_table_lookup(a->values, host); } if(v) { int_value = char2score(v->current); } 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(xml, F_ATTRD_VALUE, int_value); } send: - crm_info("Broadcasting %s[%s] = %s", host, attr, value); + crm_info("Broadcasting %s[%s] = %s%s", host, attr, value, election_state(writer) == election_won?" (writer)":""); + crm_xml_add_int(xml, F_ATTRD_WRITER, election_state(writer)); send_cluster_message(NULL, crm_msg_attrd, xml, TRUE); free(key); free(set); free(host); } - } void attrd_peer_message(crm_node_t *peer, xmlNode *xml) { const char *op = crm_element_value(xml, F_ATTRD_TASK); + const char *election_op = crm_element_value(xml, F_CRM_TASK); + + if(election_op) { + election_count_vote(writer, xml, TRUE); + return; + } + + if(election_state(writer) == election_won && safe_str_neq(peer->uname, cluster->uname)) { + int peer_state = 0; + crm_element_value_int(xml, F_ATTRD_WRITER, &peer_state); + if(peer_state == election_won) { + crm_notice("Detected another attribute writer: %s", peer->uname); + election_vote(writer); + } + } if(safe_str_eq(op, "update")) { - bool changed = FALSE; - attribute_value_t *v = NULL; + attrd_peer_update(peer, xml); - const char *host = crm_element_value(xml, F_ATTRD_HOST); - const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE); - const char *value = crm_element_value(xml, F_ATTRD_VALUE); + } else if(safe_str_eq(op, "sync")) { + attrd_peer_sync(peer, xml); - attribute_t *a = g_hash_table_lookup(attributes, attr); + } else if(safe_str_eq(op, "sync-response")) { + xmlNode *child = NULL; - if(a == NULL) { - a = create_attribute(xml); + crm_debug("Processing %s from %s", op, peer->uname); + for (child = __xml_first_child(xml); child != NULL; child = __xml_next(child)) { + attrd_peer_update(peer, child); } + } +} - v = g_hash_table_lookup(a->values, host); +void +attrd_peer_sync(crm_node_t *peer, xmlNode *xml) +{ + GHashTableIter aIter; + GHashTableIter vIter; + const char *host = NULL; - if(v == NULL) { - crm_trace("Setting %s[%s] -> %s", host, attr, value); - v = calloc(1, sizeof(attribute_value_t)); - v->current = strdup(value); - changed = TRUE; + attribute_t *a = NULL; + attribute_value_t *v = NULL; + xmlNode *sync = create_xml_node(NULL, __FUNCTION__); - } else { - crm_trace("Setting %s[%s]: %s -> %s", host, attr, v->current, value); - } + crm_xml_add(sync, F_ATTRD_TASK, "sync-response"); - if(safe_str_neq(v->current, value)) { - free(v->current); - v->current = strdup(value); - changed = TRUE; + g_hash_table_iter_init(&aIter, attributes); + while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) { + g_hash_table_iter_init(&vIter, a->values); + while (g_hash_table_iter_next(&vIter, (gpointer *) & host, (gpointer *) & v)) { + crm_debug("Syncing %s[%s] = %s to %s", a->id, host, v->current, peer->uname); + build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout, a->user, host, v->current); } + } - if(changed && a->timer) { - mainloop_timer_start(a->timer); + crm_debug("Syncing values to %s", peer?peer->uname:"everyone"); + crm_xml_add_int(sync, F_ATTRD_WRITER, election_state(writer)); + send_cluster_message(peer, crm_msg_attrd, sync, TRUE); + free_xml(sync); +} - } else if(changed) { - write_attribute(a); - } +void +attrd_peer_update(crm_node_t *peer, xmlNode *xml) +{ + bool changed = FALSE; + attribute_value_t *v = NULL; + + const char *host = crm_element_value(xml, F_ATTRD_HOST); + const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE); + const char *value = crm_element_value(xml, F_ATTRD_VALUE); + + attribute_t *a = g_hash_table_lookup(attributes, attr); + + if(a == NULL) { + a = create_attribute(xml); + } + + v = g_hash_table_lookup(a->values, host); + + if(v == NULL) { + crm_trace("Setting %s[%s] to %s from %s", host, attr, value, peer->uname); + v = calloc(1, sizeof(attribute_value_t)); + v->current = strdup(value); + changed = TRUE; + + } else { + crm_trace("Setting %s[%s]: %s -> %s from %s", host, attr, v->current, value, peer->uname); + } + + if(safe_str_neq(v->current, value)) { + free(v->current); + v->current = strdup(value); + changed = TRUE; + } + + if(changed && a->timer) { + mainloop_timer_start(a->timer); + + } else if(changed && election_state(writer) == election_won) { + write_attribute(a); } } +gboolean +attrd_election_cb(gpointer user_data) +{ + attrd_peer_sync(NULL, NULL); + return FALSE; +} + + void -attrd_peer_change_cb(void) +attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data) { + if(election_state(writer) == election_won + && kind == crm_status_nstate + && safe_str_eq(peer->state, CRM_NODE_MEMBER)) { + + attrd_peer_sync(peer, NULL); + } } static void attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data) { int level = LOG_ERR; GHashTableIter iter; const char *key = NULL; attribute_value_t *v = NULL; char *name = user_data; attribute_t *a = g_hash_table_lookup(attributes, name); if(a == NULL) { crm_info("Attribute %s no longer exists", name); goto done; } if (rc == pcmk_ok && call_id < 0) { rc = call_id; } a->updating = FALSE; switch (rc) { case pcmk_ok: level = LOG_INFO; 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 */ level = LOG_WARNING; break; } do_crm_log(level, "Update %d for %s: %s (%d)", call_id, name, pcmk_strerror(rc), rc); g_hash_table_iter_init(&iter, a->values); while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & v)) { crm_info("Update %d for %s[%s]=%s: %s (%d)", call_id, a->id, v->requested, pcmk_strerror(rc), rc); if(rc == pcmk_ok) { free(v->stored); v->stored = v->requested; v->requested = NULL; } else { free(v->requested); v->requested = NULL; } } done: free(name); - if(a && a->changed) { + if(a && a->changed && election_state(writer) == election_won) { write_attribute(a); } } +void +write_attributes(bool all) +{ + GHashTableIter iter; + attribute_t *a = NULL; + + g_hash_table_iter_init(&iter, attributes); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) { + if(all || a->changed) { + write_attribute(a); + } + } +} + void write_attribute(attribute_t *a) { int rc = pcmk_ok; xmlNode *xml_top = NULL; const char *peer = NULL; attribute_value_t *v = NULL; GHashTableIter iter; if (a == NULL) { return; } else if (the_cib == NULL) { crm_info("Delaying storing %s: cib not connected", a->id); return; } else if(a->updating) { crm_info("Delaying storing %s: update in progress", a->id); return; } a->changed = FALSE; a->updating = TRUE; xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS); g_hash_table_iter_init(&iter, a->values); while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) { crm_info("Update for %s[%s]=%s: %s (%d)", a->id, peer, v->requested, pcmk_strerror(rc), rc); if(v->current) { free(v->requested); v->requested = strdup(v->current); } else { free(v->requested); v->requested = NULL; } build_update_element(xml_top, a, peer, v->requested); } crm_log_xml_trace(xml_top, "update_attr"); rc = cib_internal_op(the_cib, CIB_OP_MODIFY, NULL, XML_CIB_TAG_STATUS, xml_top, NULL, cib_quorum_override, a->user); crm_debug("Sent update %d for %s, id=%s, set=%s", rc, a->id, a->uuid ? a->uuid : "", a->set); g_hash_table_iter_init(&iter, a->values); while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) { crm_debug("Update %d for %s[%s]=%s", rc, a->id, peer, v->requested); } the_cib->cmds->register_callback(the_cib, rc, 120, FALSE, strdup(a->id), "attrd_cib_callback", attrd_cib_callback); } bool build_update_element(xmlNode *parent, attribute_t *a, const char *node_uuid, const char *value) { xmlNode *xml_obj = NULL; xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE); crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS); crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS); crm_xml_add(xml_obj, XML_ATTR_ID, a->set); xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR); crm_xml_add(xml_obj, XML_ATTR_ID, a->uuid); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value); return TRUE; } diff --git a/attrd/internal.h b/attrd/internal.h index 66e9cd2019..c63e22deb4 100644 --- a/attrd/internal.h +++ b/attrd/internal.h @@ -1,28 +1,34 @@ /* * Copyright (C) 2013 Andrew Beekhof * * 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 */ GMainLoop *mloop; bool shutting_down; crm_cluster_t *cluster; GHashTable *attributes; +election_t *writer; -void attrd_peer_change_cb(void); void attrd_peer_message(crm_node_t *client, xmlNode *msg); void attrd_client_message(crm_client_t *client, xmlNode *msg); void free_attribute(gpointer data); +gboolean attrd_election_cb(gpointer user_data); +void attrd_peer_change_cb(enum crm_status_type type, crm_node_t *peer, const void *data); + +xmlNode *build_attribute_xml( + xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout, const char *user, + const char *peer, const char *value); diff --git a/attrd/main.c b/attrd/main.c index 8c0357e51e..b591954651 100644 --- a/attrd/main.c +++ b/attrd/main.c @@ -1,271 +1,263 @@ /* * Copyright (C) 2013 Andrew Beekhof * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include GMainLoop *mloop = NULL; bool shutting_down = FALSE; crm_cluster_t *cluster = NULL; +election_t *writer = NULL; static void attrd_shutdown(int nsig) { shutting_down = TRUE; crm_info("Shutting down"); if (mloop != NULL && g_main_is_running(mloop)) { g_main_quit(mloop); } else { crm_exit(pcmk_ok); } } static void attrd_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *groupName, uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) { uint32_t kind = 0; xmlNode *xml = NULL; const char *from = NULL; char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from); if(data == NULL) { return; } if (kind == crm_class_cluster) { xml = string2xml(data); } if (xml == NULL) { crm_err("Bad message of class %d received from %s[%u]: '%.120s'", kind, from, nodeid, data); } else { crm_node_t *peer = crm_get_peer(nodeid, from); attrd_peer_message(peer, xml); } free_xml(xml); free(data); } static void attrd_cpg_destroy(gpointer unused) { if (shutting_down) { crm_info("Corosync disconnection complete"); } else { crm_crit("Lost connection to Corosync service!"); attrd_shutdown(0); } } -static void -attrd_cpg_membership(cpg_handle_t handle, - const struct cpg_name *groupName, - const struct cpg_address *member_list, size_t member_list_entries, - const struct cpg_address *left_list, size_t left_list_entries, - const struct cpg_address *joined_list, size_t joined_list_entries) -{ - pcmk_cpg_membership(handle, groupName, - member_list, member_list_entries, - left_list, left_list_entries, - joined_list, joined_list_entries); - attrd_peer_change_cb(); -} - - static int32_t attrd_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) { crm_trace("Connection %p", c); if (shutting_down) { crm_info("Ignoring new client [%d] during shutdown", crm_ipcs_client_pid(c)); return -EPERM; } if (crm_client_new(c, uid, gid) == NULL) { return -EIO; } return 0; } static void attrd_ipc_created(qb_ipcs_connection_t * c) { crm_trace("Connection %p", c); } static int32_t attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size) { uint32_t id = 0; uint32_t flags = 0; crm_client_t *client = crm_client_get(c); xmlNode *xml = crm_ipcs_recv(client, 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(client, id, "ack", __FUNCTION__, __LINE__); } if (xml == NULL) { crm_debug("No msg from %d (%p)", crm_ipcs_client_pid(c), c); return 0; } #if ENABLE_ACL determine_request_user(client->user, xml, F_ATTRD_USER); #endif crm_trace("Processing msg from %d (%p)", crm_ipcs_client_pid(c), c); crm_log_xml_trace(xml, __PRETTY_FUNCTION__); attrd_client_message(client, xml); free_xml(xml); return 0; } /* Error code means? */ static int32_t attrd_ipc_closed(qb_ipcs_connection_t * c) { crm_client_t *client = crm_client_get(c); crm_trace("Connection %p", c); crm_client_destroy(client); return 0; } static void attrd_ipc_destroy(qb_ipcs_connection_t * c) { crm_trace("Connection %p", c); } 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 }; /* *INDENT-OFF* */ static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {0, 0, 0, 0} }; /* *INDENT-ON* */ int main(int argc, char **argv) { int flag = 0; int index = 0; int argerr = 0; qb_ipcs_service_t *ipcs = NULL; mloop = g_main_new(FALSE); crm_log_init(T_ATTRD, LOG_NOTICE, TRUE, FALSE, argc, argv, FALSE); crm_set_options(NULL, "[options]", long_options, "Daemon for aggregating and atomically storing node attribute updates into the CIB"); mainloop_add_signal(SIGTERM, attrd_shutdown); while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch (flag) { case 'V': crm_bump_log_level(argc, argv); break; case 'h': /* Help message */ crm_help(flag, EX_OK); break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', EX_USAGE); } crm_info("Starting up"); - attributes = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, free_attribute); + attributes = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_attribute); + cluster = malloc(sizeof(crm_cluster_t)); cluster->destroy = attrd_cpg_destroy; cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch; - cluster->cpg.cpg_confchg_fn = attrd_cpg_membership; + cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership; + + crm_set_status_callback(attrd_peer_change_cb); if (crm_cluster_connect(cluster) == FALSE) { crm_err("Cluster connection failed"); goto done; } crm_info("Cluster connection active"); + writer = election_init(T_ATTRD, cluster->uname, 120, attrd_election_cb); attrd_ipc_server_init(&ipcs, &ipc_callbacks); crm_info("Accepting attribute updates"); g_main_run(mloop); done: crm_notice("Cleaning up before exit"); + election_fini(writer); crm_client_disconnect_all(ipcs); qb_ipcs_destroy(ipcs); g_hash_table_destroy(attributes); return crm_exit(pcmk_ok); } diff --git a/include/crm_internal.h b/include/crm_internal.h index 62eb3852b7..853edfccec 100644 --- a/include/crm_internal.h +++ b/include/crm_internal.h @@ -1,331 +1,332 @@ /* crm_internal.h */ /* * Copyright (C) 2006 - 2008 * Andrew Beekhof * * 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 program 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 */ #ifndef CRM_INTERNAL__H # define CRM_INTERNAL__H # include # include # include # include # include # include # include # include /* Dynamic loading of libraries */ void *find_library_function(void **handle, const char *lib, const char *fn, int fatal); void *convert_const_pointer(const void *ptr); /* For ACLs */ char *uid2username(uid_t uid); void determine_request_user(const char *user, xmlNode * request, const char *field); # if ENABLE_ACL # include static inline gboolean is_privileged(const char *user) { if (user == NULL) { return FALSE; } else if (strcmp(user, CRM_DAEMON_USER) == 0) { return TRUE; } else if (strcmp(user, "root") == 0) { return TRUE; } return FALSE; } # endif /* CLI option processing*/ # ifdef HAVE_GETOPT_H # include # else # define no_argument 0 # define required_argument 1 # endif # define pcmk_option_default 0x00000 # define pcmk_option_hidden 0x00001 # define pcmk_option_paragraph 0x00002 # define pcmk_option_example 0x00004 struct crm_option { /* Fields from 'struct option' in getopt.h */ /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; /* Custom fields */ const char *desc; long flags; }; void crm_set_options(const char *short_options, const char *usage, struct crm_option *long_options, const char *app_desc); int crm_get_option(int argc, char **argv, int *index); int crm_get_option_long(int argc, char **argv, int *index, const char **longname); void crm_help(char cmd, int exit_code); /* Cluster Option Processing */ typedef struct pe_cluster_option_s { const char *name; const char *alt_name; const char *type; const char *values; const char *default_value; gboolean(*is_valid) (const char *); const char *description_short; const char *description_long; } pe_cluster_option; const char *cluster_option(GHashTable * options, gboolean(*validate) (const char *), const char *name, const char *old_name, const char *def_value); const char *get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name); void config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option * option_list, int len); void verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len); gboolean check_time(const char *value); gboolean check_timer(const char *value); gboolean check_boolean(const char *value); gboolean check_number(const char *value); /* Shared PE/crmd functionality */ void filter_action_parameters(xmlNode * param_set, const char *version); void filter_reload_parameters(xmlNode * param_set, const char *restart_string); /* Resource operation updates */ xmlNode *create_operation_update(xmlNode * parent, lrmd_event_data_t * event, const char *caller_version, int target_rc, const char *origin, int level); /* char2score */ extern int node_score_red; extern int node_score_green; extern int node_score_yellow; extern int node_score_infinity; /* Assorted convenience functions */ static inline int crm_strlen_zero(const char *s) { return !s || *s == '\0'; } char *add_list_element(char *list, const char *value); char *generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip); int get_last_sequence(const char *directory, const char *series); void write_last_sequence(const char *directory, const char *series, int sequence, int max); int crm_pid_active(long pid); void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile); gboolean crm_is_writable(const char *dir, const char *file, const char *user, const char *group, gboolean need_both); char *generate_op_key(const char *rsc_id, const char *op_type, int interval); char *generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type); char *generate_transition_magic_v202(const char *transition_key, int op_status); char *generate_transition_magic(const char *transition_key, int op_status, int op_rc); char *generate_transition_key(int action, int transition_id, int target_rc, const char *node); static inline long long crm_clear_bit(const char *function, const char *target, long long word, long long bit) { long long rc = (word & ~bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s cleared by %s", bit, target, function); } else { crm_trace("Bit 0x%.8llx cleared by %s", bit, function); } return rc; } static inline long long crm_set_bit(const char *function, const char *target, long long word, long long bit) { long long rc = (word | bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s set by %s", bit, target, function); } else { crm_trace("Bit 0x%.8llx set by %s", bit, function); } return rc; } # define set_bit(word, bit) word = crm_set_bit(__PRETTY_FUNCTION__, NULL, word, bit) # define clear_bit(word, bit) word = crm_clear_bit(__PRETTY_FUNCTION__, NULL, word, bit) void g_hash_destroy_str(gpointer data); long long crm_int_helper(const char *text, char **end_text); char *crm_concat(const char *prefix, const char *suffix, char join); char *generate_hash_key(const char *crm_msg_reference, const char *sys); bool crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len); /*! remote tcp/tls helper functions */ typedef struct crm_remote_s crm_remote_t; int crm_remote_send(crm_remote_t * remote, xmlNode * msg); int crm_remote_ready(crm_remote_t * remote, int total_timeout /*ms */ ); gboolean crm_remote_recv(crm_remote_t * remote, int total_timeout /*ms */ , int *disconnected); xmlNode *crm_remote_parse_buffer(crm_remote_t * remote); int crm_remote_tcp_connect(const char *host, int port); int crm_remote_tcp_connect_async(const char *host, int port, int timeout, /*ms */ void *userdata, void (*callback) (void *userdata, int sock)); # ifdef HAVE_GNUTLS_GNUTLS_H /*! * \internal * \brief Initiate the client handshake after establishing the tcp socket. * \note This is a blocking function, it will block until the entire handshake * is complete or until the timeout period is reached. * \retval 0 success * \retval negative, failure */ int crm_initiate_client_tls_handshake(crm_remote_t * remote, int timeout_ms); /*! * \internal * \brief Create client or server session for anon DH encryption credentials * \param sock, the socket the session will use for transport * \param type, GNUTLS_SERVER or GNUTLS_CLIENT * \param credentials, gnutls_anon_server_credentials_t or gnutls_anon_client_credentials_t * * \retval gnutls_session_t * on success * \retval NULL on failure */ void *crm_create_anon_tls_session(int sock, int type, void *credentials); /*! * \internal * \brief Create client or server session for PSK credentials * \param sock, the socket the session will use for transport * \param type, GNUTLS_SERVER or GNUTLS_CLIENT * \param credentials, gnutls_psk_server_credentials_t or gnutls_osk_client_credentials_t * * \retval gnutls_session_t * on success * \retval NULL on failure */ void *create_psk_tls_session(int csock, int type, void *credentials); # endif # define REMOTE_MSG_TERMINATOR "\r\n\r\n" const char *daemon_option(const char *option); void set_daemon_option(const char *option, const char *value); gboolean daemon_option_enabled(const char *daemon, const char *option); void strip_text_nodes(xmlNode * xml); # define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); } # define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); } # define attrd_channel T_ATTRD # define F_ATTRD_KEY "attr_key" # define F_ATTRD_ATTRIBUTE "attr_name" # define F_ATTRD_TASK "task" # define F_ATTRD_VALUE "attr_value" # define F_ATTRD_SET "attr_set" # define F_ATTRD_SECTION "attr_section" # define F_ATTRD_DAMPEN "attr_dampening" # define F_ATTRD_IGNORE_LOCALLY "attr_ignore_locally" # define F_ATTRD_HOST "attr_host" # define F_ATTRD_USER "attr_user" +# define F_ATTRD_WRITER "attr_writer" # if SUPPORT_COROSYNC # if CS_USES_LIBQB # include # include typedef struct qb_ipc_request_header cs_ipc_header_request_t; typedef struct qb_ipc_response_header cs_ipc_header_response_t; # else # include # include # include static inline int qb_to_cs_error(int a) { return a; } typedef coroipc_request_header_t cs_ipc_header_request_t; typedef coroipc_response_header_t cs_ipc_header_response_t; # endif # else typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_request_t; typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); int error __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_response_t; # endif void attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); void stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); qb_ipcs_service_t * crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb); void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb); void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw, qb_ipcs_service_t *ipcs_shm); #endif /* CRM_INTERNAL__H */