diff --git a/tools/Makefile.am b/tools/Makefile.am index 82a024f850..23edaf5748 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,81 +1,90 @@ # # heartbeat: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # # 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in ccdv INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha EXTRA_DIST = ccdv.c attrd.h apigid = @HA_APIGID@ habindir = @bindir@ halibdir = $(libdir)/@HB_PKG@ gliblib = @GLIBLIB@ habin_PROGRAMS = cl_status cl_respawn sbin_PROGRAMS = attrd_updater halib_SCRIPTS = haresources2cib.py -halib_PROGRAMS = attrd +halib_PROGRAMS = attrd pingd ## SOURCES ccdv: $(top_srcdir)/tools/ccdv.c gcc -o ccdv $(top_srcdir)/tools/ccdv.c cl_status_SOURCES = cl_status.c # A little trick. Now ccdv can be auto-built but not auto-cleaned. cl_status_DEPENDENCIES = ccdv cl_status_LDADD = $(top_builddir)/lib/hbclient/libhbclient.la \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/pils/libpils.la \ $(gliblib) \ $(top_builddir)/replace/libreplace.la cl_respawn_SOURCES = cl_respawn.c cl_respawn_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/apphb/libapphb.la \ $(top_builddir)/lib/pils/libpils.la \ $(gliblib) \ $(top_builddir)/replace/libreplace.la attrd_SOURCES = attrd.c attrd_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' attrd_LDADD = $(CRM_DEBUG_LIBS) \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/crm/common/libcrmcommon.la \ $(top_builddir)/lib/hbclient/libhbclient.la \ $(top_builddir)/lib/crm/cib/libcib.la \ $(GLIBLIB) \ $(LIBRT) +pingd_SOURCES = pingd.c +pingd_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' +pingd_LDADD = $(CRM_DEBUG_LIBS) \ + $(top_builddir)/lib/clplumbing/libplumb.la \ + $(top_builddir)/lib/crm/common/libcrmcommon.la \ + $(top_builddir)/lib/hbclient/libhbclient.la \ + $(GLIBLIB) \ + $(LIBRT) + attrd_updater_SOURCES = attrd_updater.c attrd_updater_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' attrd_updater_LDADD = $(CRM_DEBUG_LIBS) \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/crm/common/libcrmcommon.la \ $(GLIBLIB) \ $(LIBRT) install-data-hook: # install-exec-hook doesn't work (!) -chgrp $(apigid) $(DESTDIR)/$(habindir)/cl_status -chmod g+s,a-w $(DESTDIR)/$(habindir)/cl_status .PHONY: install-exec-hook diff --git a/tools/pingd.c b/tools/pingd.c new file mode 100644 index 0000000000..aa03e37b9b --- /dev/null +++ b/tools/pingd.c @@ -0,0 +1,450 @@ +/* $Id: pingd.c,v 1.1 2006/04/10 12:50:08 andrew Exp $ */ + +/* + * Copyright (C) 2004 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_GETOPT_H +# include +#endif + +/* GMainLoop *mainloop = NULL; */ +const char *crm_system_name = "pingd"; + +#define OPTARGS "V?p:a:d:s:S:h:D" + +IPC_Channel *attrd = NULL; +GMainLoop* mainloop = NULL; +GHashTable *ping_nodes = NULL; +const char *pingd_attr = "pingd"; +gboolean do_filter = FALSE; +ll_cluster_t *pingd_cluster = NULL; +gboolean need_shutdown = FALSE; + +const char *attr_set = NULL; +const char *attr_section = NULL; +const char *attr_dampen = NULL; +int attr_multiplier = 1; + +void pingd_nstatus_callback( + const char *node, const char *status, void *private_data); +void pingd_lstatus_callback( + const char *node, const char *link, const char *status, + void *private_data); +void do_node_walk(ll_cluster_t *hb_cluster); +void send_update(void); + +static gboolean +pingd_shutdown(int nsig, gpointer unused) +{ + need_shutdown = TRUE; + send_update(); + crm_info("Exiting"); + + if (mainloop != NULL && g_main_is_running(mainloop)) { + g_main_quit(mainloop); + } else { + exit(0); + } + return FALSE; +} + +static 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: This text\n", "help", '?'); + fprintf(stream, "\t--%s (-%c) \t: Daemon pid file location\n", "pid-file", 'p'); + + fflush(stream); + + exit(exit_status); +} + + +static gboolean +pingd_ha_dispatch(IPC_Channel *channel, gpointer user_data) +{ + gboolean stay_connected = TRUE; + + crm_debug_2("Invoked"); + + while(pingd_cluster != NULL && IPC_ISRCONN(channel)) { + if(pingd_cluster->llc_ops->msgready(pingd_cluster) == 0) { + crm_debug_2("no message ready yet"); + break; + } + /* invoke the callbacks but dont block */ + pingd_cluster->llc_ops->rcvmsg(pingd_cluster, 0); + } + + if (pingd_cluster == NULL || channel->ch_status != IPC_CONNECT) { + if(need_shutdown == FALSE) { + crm_crit("Lost connection to heartbeat service."); + } else { + crm_info("Lost connection to heartbeat service."); + } + stay_connected = FALSE; + } + + return stay_connected; +} + + +static void +pingd_ha_connection_destroy(gpointer user_data) +{ + crm_debug_3("Invoked"); + if(need_shutdown) { + /* we signed out, so this is expected */ + crm_info("Heartbeat disconnection complete"); + return; + } + + crm_crit("Lost connection to heartbeat service!"); +} + +static gboolean +register_with_ha(void) +{ + if(pingd_cluster == NULL) { + pingd_cluster = ll_cluster_new("heartbeat"); + } + if(pingd_cluster == NULL) { + crm_err("Cannot create heartbeat object"); + return FALSE; + } + + crm_debug("Signing in with Heartbeat"); + if (pingd_cluster->llc_ops->signon( + pingd_cluster, crm_system_name) != HA_OK) { + + crm_err("Cannot sign on with heartbeat: %s", + pingd_cluster->llc_ops->errmsg(pingd_cluster)); + crm_err("REASON: %s", pingd_cluster->llc_ops->errmsg(pingd_cluster)); + return FALSE; + } + + do_node_walk(pingd_cluster); + + crm_debug_3("Be informed of Node Status changes"); + if (HA_OK != pingd_cluster->llc_ops->set_nstatus_callback( + pingd_cluster, pingd_nstatus_callback, NULL)) { + + crm_err("Cannot set nstatus callback: %s", + pingd_cluster->llc_ops->errmsg(pingd_cluster)); + crm_err("REASON: %s", pingd_cluster->llc_ops->errmsg(pingd_cluster)); + return FALSE; + } + + if (pingd_cluster->llc_ops->set_ifstatus_callback( + pingd_cluster, pingd_lstatus_callback, NULL) != HA_OK) { + cl_log(LOG_ERR, "Cannot set if status callback"); + crm_err("REASON: %s", pingd_cluster->llc_ops->errmsg(pingd_cluster)); + return FALSE; + } + + crm_debug_3("Adding channel to mainloop"); + G_main_add_IPC_Channel( + G_PRIORITY_HIGH, pingd_cluster->llc_ops->ipcchan( + pingd_cluster), + FALSE, pingd_ha_dispatch, pingd_cluster, + pingd_ha_connection_destroy); + + return TRUE; +} + +int +main(int argc, char **argv) +{ + int lpc; + int argerr = 0; + int flag; + char *pid_file = NULL; + gboolean daemonize = FALSE; + +#ifdef HAVE_GETOPT_H + int option_index = 0; + static struct option long_options[] = { + /* Top-level Options */ + {"verbose", 0, 0, 'V'}, + {"help", 0, 0, '?'}, + {"pid-file", 1, 0, 'p'}, + {"ping-host", 1, 0, 'h'}, + {"attr-name", 1, 0, 'a'}, + {"attr-set", 1, 0, 's'}, + {"daemonize", 0, 0, 'D'}, + {"attr-section", 1, 0, 'S'}, + {"attr-dampen", 1, 0, 'd'}, + {"value-multiplier", 1, 0, 'm'}, + + {0, 0, 0, 0} + }; +#endif + pid_file = crm_strdup("/tmp/pingd.pid"); + crm_system_name = basename(argv[0]); + + G_main_add_SignalHandler( + G_PRIORITY_HIGH, SIGTERM, pingd_shutdown, NULL, NULL); + + ping_nodes = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); + + crm_log_init(crm_system_name); + + 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': + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case 'p': + pid_file = crm_strdup(optarg); + break; + case 'a': + pingd_attr = crm_strdup(optarg); + break; + case 'h': + do_filter = TRUE; + fprintf(stdout, "Adding ping host %s", optarg); + g_hash_table_insert(ping_nodes, + crm_strdup(optarg), + crm_strdup(optarg)); + break; + case 's': + attr_set = crm_strdup(optarg); + break; + case 'm': + attr_multiplier = crm_parse_int(optarg, "1"); + break; + case 'S': + attr_section = crm_strdup(optarg); + break; + case 'd': + attr_dampen = crm_strdup(optarg); + break; + case 'D': + daemonize = TRUE; + break; + default: + printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); + crm_err("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); + ++argerr; + break; + } + } + + if (optind < argc) { + crm_err("non-option ARGV-elements: "); + printf("non-option ARGV-elements: "); + while (optind < argc) { + crm_err("%s ", argv[optind++]); + printf("%s ", argv[optind++]); + } + printf("\n"); + } + if (argerr) { + usage(crm_system_name, LSB_EXIT_GENERIC); + } + + crm_make_daemon(crm_system_name, daemonize, pid_file); + + for(lpc = 0; attrd == NULL && lpc < 30; lpc++) { + crm_debug("attrd registration attempt: %d", lpc); + sleep(2); + attrd = init_client_ipc_comms_nodispatch(T_ATTRD); + } + + if(attrd == NULL) { + crm_err("attrd registration failed"); + cl_flush_logs(); + exit(LSB_EXIT_GENERIC); + } + + if(register_with_ha() == FALSE) { + crm_err("HA registration failed"); + cl_flush_logs(); + exit(LSB_EXIT_GENERIC); + } + + crm_info("Starting %s", crm_system_name); + mainloop = g_main_new(FALSE); + g_main_run(mainloop); + + crm_info("Exiting %s", crm_system_name); + return 0; +} + + +static void count_ping_nodes(gpointer key, gpointer value, gpointer user_data) +{ + int *num_active = user_data; + CRM_CHECK(num_active != NULL, return); + + if(need_shutdown) { + return; + } + + if(safe_str_eq(value, PINGSTATUS)) { + (*num_active)++; + } +} + +void +send_update(void) +{ + int num_active = 0; + HA_Message *update = ha_msg_new(4); + ha_msg_add(update, F_TYPE, T_ATTRD); + ha_msg_add(update, F_ORIG, crm_system_name); + ha_msg_add(update, F_ATTRD_TASK, "update"); + ha_msg_add(update, F_ATTRD_ATTRIBUTE, pingd_attr); + + g_hash_table_foreach(ping_nodes, count_ping_nodes, &num_active); + crm_info("%d active ping nodes", num_active); + + if(num_active > 0) { + ha_msg_add_int(update, F_ATTRD_VALUE, attr_multiplier*num_active); + } + + if(attr_set != NULL) { + ha_msg_add(update, F_ATTRD_SET, attr_set); + } + if(attr_section != NULL) { + ha_msg_add(update, F_ATTRD_SECTION, attr_section); + } + if(attr_dampen != NULL) { + ha_msg_add(update, F_ATTRD_DAMPEN, attr_dampen); + } + + if(send_ipc_message(attrd, update) == FALSE) { + crm_err("Could not send update"); + exit(1); + } +} + +void +pingd_nstatus_callback( + const char *node, const char * status, void* private_data) +{ + crm_notice("Status update: Ping node %s now has status [%s]", + node, status); + + if(g_hash_table_lookup(ping_nodes, node) != NULL) { + g_hash_table_replace( + ping_nodes, crm_strdup(node), crm_strdup(status)); + send_update(); + } +} + +void +pingd_lstatus_callback(const char *node, const char *lnk, const char *status, + void *private) +{ + crm_notice("Status update: Ping node %s now has status [%s]", + node, status); + pingd_nstatus_callback(node, status, private); +} + +void +do_node_walk(ll_cluster_t *hb_cluster) +{ + const char *ha_node = NULL; + + /* Async get client status information in the cluster */ + crm_debug_2("Invoked"); + crm_debug_3("Requesting an initial dump of CRMD client_status"); + hb_cluster->llc_ops->client_status( + hb_cluster, NULL, CRM_SYSTEM_CRMD, -1); + + crm_info("Requesting the list of configured nodes"); + hb_cluster->llc_ops->init_nodewalk(hb_cluster); + + do { + const char *ha_node_type = NULL; + const char *ha_node_status = NULL; + + ha_node = hb_cluster->llc_ops->nextnode(hb_cluster); + if(ha_node == NULL) { + continue; + } + + ha_node_type = hb_cluster->llc_ops->node_type( + hb_cluster, ha_node); + if(safe_str_neq(PINGNODE, ha_node_type)) { + crm_debug("Node %s: skipping '%s'", + ha_node, ha_node_type); + continue; + } + + if(do_filter + && g_hash_table_lookup(ping_nodes, ha_node) == NULL) { + crm_debug("Filtering: %s", ha_node); + continue; + } + + ha_node_status = hb_cluster->llc_ops->node_status( + hb_cluster, ha_node); + + crm_debug("Adding: %s=%s", ha_node, ha_node_status); + g_hash_table_replace(ping_nodes, crm_strdup(ha_node), + crm_strdup(ha_node_status)); + + } while(ha_node != NULL); + + hb_cluster->llc_ops->end_nodewalk(hb_cluster); + crm_debug_2("Complete"); + send_update(); +}