diff options
Diffstat (limited to '')
-rw-r--r-- | agents/kdump/fence_kdump.c | 592 | ||||
-rw-r--r-- | agents/kdump/fence_kdump_send.8 | 50 | ||||
-rw-r--r-- | agents/kdump/fence_kdump_send.c | 255 | ||||
-rw-r--r-- | agents/kdump/list.h | 573 | ||||
-rw-r--r-- | agents/kdump/message.h | 41 | ||||
-rw-r--r-- | agents/kdump/options.h | 260 | ||||
-rw-r--r-- | agents/kdump/version.h | 33 |
7 files changed, 1804 insertions, 0 deletions
diff --git a/agents/kdump/fence_kdump.c b/agents/kdump/fence_kdump.c new file mode 100644 index 0000000..eda1559 --- /dev/null +++ b/agents/kdump/fence_kdump.c @@ -0,0 +1,592 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) Ryan O'Hara (rohara@redhat.com) + * Copyright (c) Red Hat, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <syslog.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include "options.h" +#include "message.h" +#include "version.h" + +static int verbose = 0; + +#define log_debug(lvl, fmt, args...) \ +do { \ + if (lvl <= verbose) { \ + fprintf (stdout, "[debug]: " fmt, ##args); \ + syslog (LOG_INFO, fmt, ##args); \ + } \ +} while (0); + +#define log_error(lvl, fmt, args...) \ +do { \ + if (lvl <= verbose) { \ + fprintf (stderr, "[error]: " fmt, ##args); \ + syslog (LOG_ERR, fmt, ##args); \ + } \ +} while (0); + +static int +trim (char *str) +{ + char *p; + int len; + + if (!str) return (0); + + len = strlen (str); + + while (len--) { + if (isspace (str[len])) { + str[len] = 0; + } else { + break; + } + } + + for (p = str; *p && isspace (*p); p++); + + memmove (str, p, strlen (p) + 1); + + return (strlen (str)); +} + +static int +do_action_monitor (void) +{ + const char cmdline_path[] = "/proc/cmdline"; + FILE *procFile; + size_t sz = 0; + char *lines = NULL; + int result = 1; + + procFile = fopen(cmdline_path, "r"); + + if (procFile == NULL) { + log_error (0, "Unable to open file %s (%s)\n", cmdline_path, strerror (errno)); + return 1; + } + + while (!feof (procFile)) { + ssize_t rv = getline (&lines, &sz, procFile); + if ((rv != -1) && (strstr(lines, "crashkernel=") != NULL)) { + result = 0; + } + } + + free (lines); + fclose (procFile); + + return result; +} + +static int +do_action_off (const fence_kdump_opts_t *opts) +{ + int error; + fd_set rfds; + fence_kdump_msg_t msg; + fence_kdump_node_t *node; + struct timeval timeout; + struct addrinfo hints; + fence_kdump_node_t *check_node; + char addr[NI_MAXHOST]; + char port[NI_MAXSERV]; + struct sockaddr_storage ss; + socklen_t size = sizeof (ss); + + if (list_empty (&opts->nodes)) { + return (1); + } else { + node = list_first_entry (&opts->nodes, fence_kdump_node_t, list); + } + + timeout.tv_sec = opts->timeout; + timeout.tv_usec = 0; + + FD_ZERO (&rfds); + FD_SET (node->socket, &rfds); + + // create listening socket + memset (&hints, 0, sizeof (hints)); + + hints.ai_family = opts->family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_NUMERICSERV; + + hints.ai_family = node->info->ai_family; + hints.ai_flags |= AI_PASSIVE; + + freeaddrinfo (node->info); + + node->info = NULL; + error = getaddrinfo (NULL, node->port, &hints, &node->info); + if (error != 0) { + log_error (2, "getaddrinfo (%s)\n", gai_strerror (error)); + free_node (node); + return (1); + } + + error = bind (node->socket, node->info->ai_addr, node->info->ai_addrlen); + if (error != 0) { + log_error (2, "bind (%s)\n", strerror (errno)); + free_node (node); + return (1); + } + + list_for_each_entry (check_node, &opts->nodes, list) { + log_debug (0, "waiting for message from '%s'\n", check_node->addr); + if (node->info->ai_family != check_node->info->ai_family) { + log_error (0, "mixing IPv4 and IPv6 nodes is not supported\n"); + return (1); + } + } + + for (;;) { + error = select (node->socket + 1, &rfds, NULL, NULL, &timeout); + if (error < 0) { + log_error (2, "select (%s)\n", strerror (errno)); + break; + } + if (error == 0) { + log_debug (0, "timeout after %d seconds\n", opts->timeout); + break; + } + + error = recvfrom (node->socket, &msg, sizeof (msg), 0, (struct sockaddr *) &ss, &size); + if (error < 0) { + log_error (2, "recvfrom (%s)\n", strerror (errno)); + continue; + } + + error = getnameinfo ((struct sockaddr *) &ss, size, + addr, sizeof (addr), + port, sizeof (port), + NI_NUMERICHOST | NI_NUMERICSERV); + if (error != 0) { + log_error (2, "getnameinfo (%s)\n", gai_strerror (error)); + continue; + } + + if (msg.magic != FENCE_KDUMP_MAGIC) { + log_debug (1, "invalid magic number '0x%X'\n", msg.magic); + continue; + } + + // check if we have matched messages from any known node + list_for_each_entry (check_node, &opts->nodes, list) { + error = strcasecmp (check_node->addr, addr); + if (error == 0 ) { + switch (msg.version) { + case FENCE_KDUMP_MSGV1: + log_debug (0, "received valid message from '%s'\n", addr); + return (0); + default: + log_debug (1, "invalid message version '0x%X'\n", msg.version); + continue; + } + } + } + log_debug (1, "discard message from '%s'\n", addr); + + } + + return (1); +} + +static int +do_action_metadata (const char *self) +{ + fprintf (stdout, "<?xml version=\"1.0\" ?>\n"); + fprintf (stdout, "<resource-agent name=\"%s\"", basename (self)); + fprintf (stdout, " shortdesc=\"fencing agent for use with kdump crash recovery service\">\n"); + fprintf (stdout, "<longdesc>"); + fprintf (stdout, "fence_kdump is an I/O fencing agent to be used with the kdump\n" + "crash recovery service. When the fence_kdump agent is invoked,\n" + "it will listen for a message from the failed node that acknowledges\n" + "that the failed node is executing the kdump crash kernel.\n" + "Note that fence_kdump is not a replacement for traditional\n" + "fencing methods. The fence_kdump agent can only detect that a\n" + "node has entered the kdump crash recovery service. This allows the\n" + "kdump crash recovery service complete without being preempted by\n" + "traditional power fencing methods.\n\n" + "Note: the \"off\" action listen for message from failed node that\n" + "acknowledges node has entered kdump crash recovery service. If a valid\n" + "message is received from the failed node, the node is considered to be\n" + "fenced and the agent returns success. Failure to receive a valid\n" + "message from the failed node in the given timeout period results in\n" + "fencing failure. When multiple node names/IP addresses are specified\n" + "a single valid message is sufficient for success. This is useful when\n" + "single node can send message via several different IP addresses.\n"); + fprintf (stdout, "</longdesc>\n"); + fprintf (stdout, "<vendor-url>http://www.kernel.org/pub/linux/utils/kernel/kexec/</vendor-url>\n"); + + fprintf (stdout, "<parameters>\n"); + + fprintf (stdout, "\t<parameter name=\"nodename\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-n, --nodename=NODE[,NODE...]\" />\n"); + fprintf (stdout, "\t\t<content type=\"string\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "List of names or IP addresses of node to be fenced. This option is\n" + "required for the \"off\" action. Multiple values separated by commas\n" + "can be specified. All values must be of same IP network family." ); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "\t<parameter name=\"ipport\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-p, --ipport=PORT\" />\n"); + fprintf (stdout, "\t\t<content type=\"string\" default=\"7410\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "IP port number that the fence_kdump agent will use to listen for\n" + "messages."); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "\t<parameter name=\"family\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-f, --family=FAMILY\" />\n"); + fprintf (stdout, "\t\t<content type=\"string\" default=\"auto\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "IP network family. Force the fence_kdump agent to use a specific\n" + "family. The value for FAMILY can be \"auto\", \"ipv4\", or\n" + "\"ipv6\"."); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "\t<parameter name=\"action\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-o, --action=ACTION\" />\n"); + fprintf (stdout, "\t\t<content type=\"string\" default=\"off\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "Fencing action to perform. The value for ACTION can be either\n" + "\"off\" or \"metadata\"."); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "\t<parameter name=\"timeout\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-t, --timeout=TIMEOUT\" />\n"); + fprintf (stdout, "\t\t<content type=\"string\" default=\"60\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "Number of seconds to wait for message from failed node. If no message\n" + "is received within TIMEOUT seconds, the fence_kdump agent\n" + "returns failure."); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "\t<parameter name=\"verbose\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-v, --verbose\" />\n"); + fprintf (stdout, "\t\t<content type=\"boolean\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "Print verbose output"); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "\t<parameter name=\"version\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-V, --version\" />\n"); + fprintf (stdout, "\t\t<content type=\"boolean\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "Print version"); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "\t<parameter name=\"usage\" unique=\"0\" required=\"0\">\n"); + fprintf (stdout, "\t\t<getopt mixed=\"-h, --help\" />\n"); + fprintf (stdout, "\t\t<content type=\"boolean\" />\n"); + fprintf (stdout, "\t\t<shortdesc lang=\"en\">%s</shortdesc>\n", + "Print usage"); + fprintf (stdout, "\t</parameter>\n"); + + fprintf (stdout, "</parameters>\n"); + + fprintf (stdout, "<actions>\n"); + fprintf (stdout, "\t<action name=\"off\" />\n"); + fprintf (stdout, "\t<action name=\"monitor\" />\n"); + fprintf (stdout, "\t<action name=\"metadata\" />\n"); + fprintf (stdout, "\t<action name=\"validate-all\" />\n"); + fprintf (stdout, "</actions>\n"); + + fprintf (stdout, "</resource-agent>\n"); + + return (0); +} + +static void +print_usage (const char *self) +{ + fprintf (stdout, "Usage: %s [options]\n", basename (self)); + fprintf (stdout, "\n"); + fprintf (stdout, "Options:\n"); + fprintf (stdout, "\n"); + fprintf (stdout, "%s\n", + " -n, --nodename=NODE[,NODE...]List of names or IP addresses of node to be fenced"); + fprintf (stdout, "%s\n", + " -p, --ipport=PORT IP port number (default: 7410)"); + fprintf (stdout, "%s\n", + " -f, --family=FAMILY Network family: ([auto], ipv4, ipv6)"); + fprintf (stdout, "%s\n", + " -o, --action=ACTION Fencing action: ([off], monitor, metadata, validate-all)"); + fprintf (stdout, "%s\n", + " -t, --timeout=TIMEOUT Timeout in seconds (default: 60)"); + fprintf (stdout, "%s\n", + " -v, --verbose Print verbose output"); + fprintf (stdout, "%s\n", + " -V, --version Print version"); + fprintf (stdout, "%s\n", + " -h, --help Print usage"); + fprintf (stdout, "\n"); + + return; +} + +static int +get_options_node (fence_kdump_opts_t *opts) +{ + int error; + struct addrinfo hints; + fence_kdump_node_t *node; + + node = malloc (sizeof (fence_kdump_node_t)); + if (!node) { + log_error (2, "malloc (%s)\n", strerror (errno)); + return (1); + } + + memset (node, 0, sizeof (fence_kdump_node_t)); + memset (&hints, 0, sizeof (hints)); + + hints.ai_family = opts->family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_NUMERICSERV; + + strncpy (node->name, opts->nodename, sizeof (node->name) - 1); + snprintf (node->port, sizeof (node->port), "%d", opts->ipport); + + node->info = NULL; + error = getaddrinfo (node->name, node->port, &hints, &node->info); + if (error != 0) { + log_error (2, "getaddrinfo (%s)\n", gai_strerror (error)); + free_node (node); + return (1); + } + + error = getnameinfo (node->info->ai_addr, node->info->ai_addrlen, + node->addr, sizeof (node->addr), + node->port, sizeof (node->port), + NI_NUMERICHOST | NI_NUMERICSERV); + if (error != 0) { + log_error (2, "getnameinfo (%s)\n", gai_strerror (error)); + free_node (node); + return (1); + } + + node->socket = socket (node->info->ai_family, + node->info->ai_socktype, + node->info->ai_protocol); + if (node->socket < 0) { + log_error (2, "socket (%s)\n", strerror (errno)); + free_node (node); + return (1); + } + + list_add_tail (&node->list, &opts->nodes); + + return (0); +} + +static void +get_options (int argc, char **argv, fence_kdump_opts_t *opts) +{ + int opt; + + struct option options[] = { + { "nodename", required_argument, NULL, 'n' }, + { "ipport", required_argument, NULL, 'p' }, + { "family", required_argument, NULL, 'f' }, + { "action", required_argument, NULL, 'o' }, + { "timeout", required_argument, NULL, 't' }, + { "verbose", optional_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + while ((opt = getopt_long (argc, argv, "n:p:f:o:t:v::Vh", options, NULL)) != EOF) { + switch (opt) { + case 'n': + set_option_nodename (opts, optarg); + break; + case 'p': + set_option_ipport (opts, optarg); + break; + case 'f': + set_option_family (opts, optarg); + break; + case 'o': + set_option_action (opts, optarg); + break; + case 't': + set_option_timeout (opts, optarg); + break; + case 'v': + set_option_verbose (opts, optarg); + break; + case 'V': + print_version (argv[0]); + exit (0); + case 'h': + print_usage (argv[0]); + exit (0); + default: + print_usage (argv[0]); + exit (1); + } + } + + verbose = opts->verbose; + + return; +} + +static void +get_options_stdin (fence_kdump_opts_t *opts) +{ + char buf[1024]; + char *opt; + char *arg; + + while (fgets (buf, sizeof (buf), stdin) != 0) { + if (trim (buf) == 0) { + continue; + } + if (buf[0] == '#') { + continue; + } + + opt = buf; + + if ((arg = strchr (opt, '=')) != 0) { + *arg = 0; + arg += 1; + } else { + continue; + } + + if (!strcasecmp (opt, "nodename")) { + set_option_nodename (opts, arg); + continue; + } + if (!strcasecmp (opt, "ipport")) { + set_option_ipport (opts, arg); + continue; + } + if (!strcasecmp (opt, "family")) { + set_option_family (opts, arg); + continue; + } + if (!strcasecmp (opt, "action")) { + set_option_action (opts, arg); + continue; + } + if (!strcasecmp (opt, "timeout")) { + set_option_timeout (opts, arg); + continue; + } + if (!strcasecmp (opt, "verbose")) { + set_option_verbose (opts, arg); + continue; + } + } + + verbose = opts->verbose; + + return; +} + +int +main (int argc, char **argv) +{ + int error = 1; + fence_kdump_opts_t opts; + char *ptr; + char *node_list; + + init_options (&opts); + + if (argc > 1) { + get_options (argc, argv, &opts); + } else { + get_options_stdin (&opts); + } + + openlog ("fence_kdump", LOG_CONS|LOG_PID, LOG_DAEMON); + + if (opts.action == FENCE_KDUMP_ACTION_OFF) { + if (opts.nodename == NULL) { + log_error (0, "action 'off' requires nodename\n"); + exit (1); + } + node_list = (char *)malloc(strlen(opts.nodename)+1); + + strcpy(node_list, opts.nodename); //make local copy of nodename on which we can safely iterate + // iterate through node_list + for (ptr = strtok(node_list, ","); ptr != NULL; ptr = strtok(NULL, ",")) { + set_option_nodename (&opts, ptr); //overwrite nodename for next function + if (get_options_node (&opts) != 0) { + log_error (0, "failed to get node '%s'\n", opts.nodename); + exit (1); + } + } + free(node_list); + } + + if (verbose != 0) { + //clear nodename to avoid showing just last nodename here + free(opts.nodename); + opts.nodename = NULL; + print_options (&opts); + } + + switch (opts.action) { + case FENCE_KDUMP_ACTION_OFF: + error = do_action_off (&opts); + break; + case FENCE_KDUMP_ACTION_METADATA: + error = do_action_metadata (argv[0]); + break; + case FENCE_KDUMP_ACTION_MONITOR: + error = do_action_monitor (); + break; + case FENCE_KDUMP_ACTION_VALIDATE: + error = 0; + break; + default: + break; + } + + free_options (&opts); + + return (error); +} diff --git a/agents/kdump/fence_kdump_send.8 b/agents/kdump/fence_kdump_send.8 new file mode 100644 index 0000000..ab95836 --- /dev/null +++ b/agents/kdump/fence_kdump_send.8 @@ -0,0 +1,50 @@ +.TH fence_kdump_send 8 +.SH NAME +fence_kdump_send - send kdump acknowlegement message to cluster nodes +.SH SYNOPSIS +.B +fence_kdump_send +[\fIOPTIONS]\fR... [NODE]... +.SH DESCRIPTION +\fIfence_kdump_send\fP is a utility used to send messages that +acknowledge that the node has entered the kdump crash recovery +service. This utility is intended to be used the the \fIfence_kdump\fP +agent as a means detect that a failed node has entered the kdump crash +recovery service. +The \fIfence_kdump_send\fP utility is typically run from within the +kdump kernel after a cluster node has encountered a kernel panic. Once +the cluster node has entered the kdump crash recovery service, +\fIfence_kdump_send\fP will periodically send messages to all cluster +nodes. When the \fIfence_kdump\fP agent receives a valid message from +the failed node, fencing is complete. +.SH OPTIONS +.TP +.B -p, --ipport=\fIPORT\fP +IP port number that the \fIfence_kdump\fP agent is using to listen for +messages. (default: 7410) +.TP +.B -f, --family=\fIFAMILY\fP +IP network family. Force the \fIfence_kdump_send\fP utility to use a +particular network family. Value for \fIFAMILY\fP can be "auto", +"ipv4", or "ipv6". (default: auto) +.TP +.B -c, --count=\fICOUNT\fP +Number of messages to send. If \fICOUNT\fP is zero, +\fIfence_kdump_send\fP will send messages indefinitely. (default: 0) +.TP +.B -i, --interval=\fIINTERVAL\fP +Time to wait between sending a message. The value for \fIINTERVAL\fP +must be greater than zero. (default: 10) +.TP +.B -v, --verbose +Print verbose output. +.TP +.B -V, --version +Print version and exit. +.TP +.B -h, --help +Print usage and exit. +.SH AUTHOR +Ryan O'Hara <rohara@redhat.com> +.SH SEE ALSO +fence_kdump(8), mkdumprd(8), kdump.conf(5) diff --git a/agents/kdump/fence_kdump_send.c b/agents/kdump/fence_kdump_send.c new file mode 100644 index 0000000..638f8c9 --- /dev/null +++ b/agents/kdump/fence_kdump_send.c @@ -0,0 +1,255 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) Ryan O'Hara (rohara@redhat.com) + * Copyright (c) Red Hat, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <syslog.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include "options.h" +#include "message.h" +#include "version.h" + +static int verbose = 0; + +#define log_debug(lvl, fmt, args...) \ +do { \ + if (lvl <= verbose) \ + fprintf (stdout, "[debug]: " fmt, ##args); \ +} while (0); + +#define log_error(lvl, fmt, args...) \ +do { \ + if (lvl <= verbose) \ + fprintf (stderr, "[error]: " fmt, ##args); \ +} while (0); + +static int +send_message (const fence_kdump_node_t *node, void *msg, int len) +{ + int error; + + error = sendto (node->socket, msg, len, 0, node->info->ai_addr, node->info->ai_addrlen); + if (error < 0) { + log_error (2, "sendto (%s)\n", strerror (errno)); + goto out; + } + + log_debug (1, "message sent to node '%s'\n", node->addr); + +out: + return (error); +} + +static void +print_usage (const char *self) +{ + fprintf (stdout, "Usage: %s [options] [nodes]\n", basename (self)); + fprintf (stdout, "\n"); + fprintf (stdout, "Options:\n"); + fprintf (stdout, "\n"); + fprintf (stdout, "%s\n", + " -p, --ipport=PORT Port number (default: 7410)"); + fprintf (stdout, "%s\n", + " -f, --family=FAMILY Network family ([auto], ipv4, ipv6)"); + fprintf (stdout, "%s\n", + " -c, --count=COUNT Number of messages to send (default: 0)"); + fprintf (stdout, "%s\n", + " -i, --interval=INTERVAL Interval in seconds (default: 10)"); + fprintf (stdout, "%s\n", + " -v, --verbose Print verbose output"); + fprintf (stdout, "%s\n", + " -V, --version Print version"); + fprintf (stdout, "%s\n", + " -h, --help Print usage"); + fprintf (stdout, "\n"); + + return; +} + +static int +get_options_node (fence_kdump_opts_t *opts) +{ + int error; + struct addrinfo hints; + fence_kdump_node_t *node; + + node = malloc (sizeof (fence_kdump_node_t)); + if (!node) { + log_error (2, "malloc (%s)\n", strerror (errno)); + return (1); + } + + memset (node, 0, sizeof (fence_kdump_node_t)); + memset (&hints, 0, sizeof (hints)); + + hints.ai_family = opts->family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_NUMERICSERV; + + strncpy (node->name, opts->nodename, sizeof (node->name) - 1); + snprintf (node->port, sizeof (node->port), "%d", opts->ipport); + + node->info = NULL; + error = getaddrinfo (node->name, node->port, &hints, &node->info); + if (error != 0) { + log_error (2, "getaddrinfo (%s)\n", gai_strerror (error)); + free_node (node); + return (1); + } + + error = getnameinfo (node->info->ai_addr, node->info->ai_addrlen, + node->addr, sizeof (node->addr), + node->port, sizeof (node->port), + NI_NUMERICHOST | NI_NUMERICSERV); + if (error != 0) { + log_error (2, "getnameinfo (%s)\n", gai_strerror (error)); + free_node (node); + return (1); + } + + node->socket = socket (node->info->ai_family, + node->info->ai_socktype, + node->info->ai_protocol); + if (node->socket < 0) { + log_error (2, "socket (%s)\n", strerror (errno)); + free_node (node); + return (1); + } + + list_add_tail (&node->list, &opts->nodes); + + return (0); +} + +static void +get_options (int argc, char **argv, fence_kdump_opts_t *opts) +{ + int opt; + + struct option options[] = { + { "ipport", required_argument, NULL, 'p' }, + { "family", required_argument, NULL, 'f' }, + { "count", required_argument, NULL, 'c' }, + { "interval", required_argument, NULL, 'i' }, + { "verbose", optional_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + while ((opt = getopt_long (argc, argv, "p:f:c:i:v::Vh", options, NULL)) != EOF) { + switch (opt) { + case 'p': + set_option_ipport (opts, optarg); + break; + case 'f': + set_option_family (opts, optarg); + break; + case 'c': + set_option_count (opts, optarg); + break; + case 'i': + set_option_interval (opts, optarg); + break; + case 'v': + set_option_verbose (opts, optarg); + break; + case 'V': + print_version (argv[0]); + exit (0); + case 'h': + print_usage (argv[0]); + exit (0); + default: + print_usage (argv[0]); + exit (1); + } + } + + verbose = opts->verbose; + + return; +} + +int +main (int argc, char **argv) +{ + int count = 1; + fence_kdump_msg_t msg; + fence_kdump_opts_t opts; + fence_kdump_node_t *node; + + init_options (&opts); + + if (argc > 1) { + get_options (argc, argv, &opts); + } else { + print_usage (argv[0]); + exit (1); + } + + for (; optind < argc; optind++) { + opts.nodename = argv[optind]; + if (get_options_node (&opts) != 0) { + log_error (1, "failed to get node '%s'\n", opts.nodename); + } + opts.nodename = NULL; + } + + if (list_empty (&opts.nodes)) { + print_usage (argv[0]); + exit (1); + } + + if (verbose != 0) { + print_options (&opts); + } + + init_message (&msg); + + for (;;) { + list_for_each_entry (node, &opts.nodes, list) { + send_message (node, &msg, sizeof (msg)); + } + + if ((opts.count != 0) && (++count > opts.count)) { + break; + } + + sleep (opts.interval); + } + + free_options (&opts); + + return (0); +} diff --git a/agents/kdump/list.h b/agents/kdump/list.h new file mode 100644 index 0000000..8945a62 --- /dev/null +++ b/agents/kdump/list.h @@ -0,0 +1,573 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include <stddef.h> + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, + struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, + struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del_entry(entry); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + + return (next == head) && (next == head->prev); +} + +/** + * list_rotate_left - rotate the list to the left + * @head: the head of the list + */ +static inline void list_rotate_left(struct list_head *head) +{ + struct list_head *first; + + if (!list_empty(head)) { + first = head->next; + list_move_tail(first, head); + } +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, + struct list_head *entry) +{ + struct list_head *new_first = entry->next; + + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, + struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && + (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, + struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + last->next = next; + next->prev = last; +} + +/** + * list_splice - join two lists, this is designed for stacks + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(const struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); + } +} + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue - continue list iteration safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from - iterate over list from current point safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/** + * list_safe_reset_next - reset a stale list_for_each_entry_safe loop + * @pos: the loop cursor used in the list_for_each_entry_safe loop + * @n: temporary storage used in list_for_each_entry_safe + * @member: the name of the list_struct within the struct. + * + * list_safe_reset_next is not safe to use in general if the list may be + * modified concurrently (eg. the lock is dropped in the loop body). An + * exception to this is if the cursor element (pos) is pinned in the list, + * and list_safe_reset_next is called after re-taking the lock and before + * completing the current iteration of the loop body. + */ +#define list_safe_reset_next(pos, n, member) \ + n = list_entry(pos->member.next, typeof(*pos), member) + +#endif /* _LINUX_LIST_H */ diff --git a/agents/kdump/message.h b/agents/kdump/message.h new file mode 100644 index 0000000..2c82229 --- /dev/null +++ b/agents/kdump/message.h @@ -0,0 +1,41 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) Ryan O'Hara (rohara@redhat.com) + * Copyright (c) Red Hat, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _FENCE_KDUMP_MESSAGE_H +#define _FENCE_KDUMP_MESSAGE_H + +#define FENCE_KDUMP_MAGIC 0x1B302A40 + +#define FENCE_KDUMP_MSGV1 0x1 + +typedef struct __attribute__ ((packed)) fence_kdump_msg { + uint32_t magic; + uint32_t version; +} fence_kdump_msg_t; + +static inline void +init_message (fence_kdump_msg_t *msg) +{ + msg->magic = FENCE_KDUMP_MAGIC; + msg->version = FENCE_KDUMP_MSGV1; +} + +#endif /* _FENCE_KDUMP_MESSAGE_H */ diff --git a/agents/kdump/options.h b/agents/kdump/options.h new file mode 100644 index 0000000..3cf7b43 --- /dev/null +++ b/agents/kdump/options.h @@ -0,0 +1,260 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) Ryan O'Hara (rohara@redhat.com) + * Copyright (c) Red Hat, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _FENCE_KDUMP_OPTIONS_H +#define _FENCE_KDUMP_OPTIONS_H + +#include "list.h" + +#define FENCE_KDUMP_NAME_LEN 256 +#define FENCE_KDUMP_ADDR_LEN 46 +#define FENCE_KDUMP_PORT_LEN 6 + +enum { + FENCE_KDUMP_ACTION_OFF = 0, + FENCE_KDUMP_ACTION_ON = 1, + FENCE_KDUMP_ACTION_REBOOT = 2, + FENCE_KDUMP_ACTION_STATUS = 3, + FENCE_KDUMP_ACTION_LIST = 4, + FENCE_KDUMP_ACTION_MONITOR = 5, + FENCE_KDUMP_ACTION_METADATA = 6, + FENCE_KDUMP_ACTION_VALIDATE = 7, +}; + +enum { + FENCE_KDUMP_FAMILY_AUTO = AF_UNSPEC, + FENCE_KDUMP_FAMILY_IPV6 = AF_INET6, + FENCE_KDUMP_FAMILY_IPV4 = AF_INET, +}; + +#define FENCE_KDUMP_DEFAULT_IPPORT 7410 +#define FENCE_KDUMP_DEFAULT_FAMILY 0 +#define FENCE_KDUMP_DEFAULT_ACTION 0 +#define FENCE_KDUMP_DEFAULT_COUNT 0 +#define FENCE_KDUMP_DEFAULT_INTERVAL 10 +#define FENCE_KDUMP_DEFAULT_TIMEOUT 60 +#define FENCE_KDUMP_DEFAULT_VERBOSE 0 + +typedef struct fence_kdump_opts { + char *nodename; + int ipport; + int family; + int action; + int count; + int interval; + int timeout; + int verbose; + struct list_head nodes; +} fence_kdump_opts_t; + +typedef struct fence_kdump_node { + char name[FENCE_KDUMP_NAME_LEN]; + char addr[FENCE_KDUMP_ADDR_LEN]; + char port[FENCE_KDUMP_PORT_LEN]; + int socket; + struct addrinfo *info; + struct list_head list; +} fence_kdump_node_t; + +static inline void +init_node (fence_kdump_node_t *node) +{ + node->info = NULL; +} + +static inline void +free_node (fence_kdump_node_t *node) +{ + freeaddrinfo (node->info); + free (node); +} + +static inline void +print_node (const fence_kdump_node_t *node) +{ + fprintf (stdout, "[debug]: node { \n"); + fprintf (stdout, "[debug]: name = %s\n", node->name); + fprintf (stdout, "[debug]: addr = %s\n", node->addr); + fprintf (stdout, "[debug]: port = %s\n", node->port); + fprintf (stdout, "[debug]: info = %p\n", node->info); + fprintf (stdout, "[debug]: } \n"); +} + +static inline void +init_options (fence_kdump_opts_t *opts) +{ + opts->nodename = NULL; + opts->ipport = FENCE_KDUMP_DEFAULT_IPPORT; + opts->family = FENCE_KDUMP_DEFAULT_FAMILY; + opts->action = FENCE_KDUMP_DEFAULT_ACTION; + opts->count = FENCE_KDUMP_DEFAULT_COUNT; + opts->interval = FENCE_KDUMP_DEFAULT_INTERVAL; + opts->timeout = FENCE_KDUMP_DEFAULT_TIMEOUT; + opts->verbose = FENCE_KDUMP_DEFAULT_VERBOSE; + + INIT_LIST_HEAD (&opts->nodes); +} + +static inline void +free_options (fence_kdump_opts_t *opts) +{ + fence_kdump_node_t *node; + fence_kdump_node_t *safe; + + list_for_each_entry_safe (node, safe, &opts->nodes, list) { + list_del (&node->list); + free_node (node); + } + + free (opts->nodename); +} + +static inline void +print_options (fence_kdump_opts_t *opts) +{ + fence_kdump_node_t *node; + + fprintf (stdout, "[debug]: options { \n"); + fprintf (stdout, "[debug]: nodename = %s\n", opts->nodename); + fprintf (stdout, "[debug]: ipport = %d\n", opts->ipport); + fprintf (stdout, "[debug]: family = %d\n", opts->family); + fprintf (stdout, "[debug]: count = %d\n", opts->count); + fprintf (stdout, "[debug]: interval = %d\n", opts->interval); + fprintf (stdout, "[debug]: timeout = %d\n", opts->timeout); + fprintf (stdout, "[debug]: verbose = %d\n", opts->verbose); + fprintf (stdout, "[debug]: } \n"); + + list_for_each_entry (node, &opts->nodes, list) { + print_node (node); + } +} + +static inline void +set_option_nodename (fence_kdump_opts_t *opts, const char *arg) +{ + if (opts->nodename != NULL) { + free (opts->nodename); + } + + opts->nodename = strdup (arg); +} + +static inline void +set_option_ipport (fence_kdump_opts_t *opts, const char *arg) +{ + opts->ipport = atoi (arg); + + if ((opts->ipport < 1) || (opts->ipport > 65535)) { + fprintf (stderr, "[error]: invalid ipport '%s'\n", arg); + exit (1); + } +} + +static inline void +set_option_family (fence_kdump_opts_t *opts, const char *arg) +{ + if (!strcasecmp (arg, "auto")) { + opts->family = FENCE_KDUMP_FAMILY_AUTO; + } else if (!strcasecmp (arg, "ipv6")) { + opts->family = FENCE_KDUMP_FAMILY_IPV6; + } else if (!strcasecmp (arg, "ipv4")) { + opts->family = FENCE_KDUMP_FAMILY_IPV4; + } else { + fprintf (stderr, "[error]: unsupported family '%s'\n", arg); + exit (1); + } +} + +static inline void +set_option_action (fence_kdump_opts_t *opts, const char *arg) +{ + if (!strcasecmp (arg, "off")) { + opts->action = FENCE_KDUMP_ACTION_OFF; + } else if (!strcasecmp (arg, "metadata")) { + opts->action = FENCE_KDUMP_ACTION_METADATA; + } else if (!strcasecmp (arg, "monitor")) { + opts->action = FENCE_KDUMP_ACTION_MONITOR; + } else if (!strcasecmp (arg, "validate-all")) { + opts->action = FENCE_KDUMP_ACTION_VALIDATE; + } else { + fprintf (stderr, "[error]: unsupported action '%s'\n", arg); + exit (1); + } +} + +static inline void +set_option_count (fence_kdump_opts_t *opts, const char *arg) +{ + opts->count = atoi (arg); + + if (opts->count < 0) { + fprintf (stderr, "[error]: invalid count '%s'\n", arg); + exit (1); + } +} + +static inline void +set_option_interval (fence_kdump_opts_t *opts, const char *arg) +{ + opts->interval = atoi (arg); + + if (opts->interval < 1) { + fprintf (stderr, "[error]: invalid interval '%s'\n", arg); + exit (1); + } +} + +static inline void +set_option_timeout (fence_kdump_opts_t *opts, const char *arg) +{ + opts->timeout = atoi (arg); + + if (opts->timeout < 1) { + fprintf (stderr, "[error]: invalid timeout '%s'\n", arg); + exit (1); + } +} + +static inline void +set_option_verbose (fence_kdump_opts_t *opts, const char *arg) +{ + int i; + + if (arg != NULL) { + if (isdigit(arg[0])) { + opts->verbose += atoi (arg); + } else { + opts->verbose += 1; /* initial -v */ + for (i = 0; i < strlen(arg); i++) { + if (arg[i] == 'v') { + opts->verbose += 1; + } else { + fprintf (stderr, "[error]: invalid value '%c' for verbose\n", arg[i]); + return; + } + } + } + } else { + opts->verbose += 1; + } +} + +#endif /* _FENCE_KDUMP_OPTIONS_H */ diff --git a/agents/kdump/version.h b/agents/kdump/version.h new file mode 100644 index 0000000..ed178b1 --- /dev/null +++ b/agents/kdump/version.h @@ -0,0 +1,33 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) Ryan O'Hara (rohara@redhat.com) + * Copyright (c) Red Hat, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _FENCE_KDUMP_VERSION_H +#define _FENCE_KDUMP_VERSION_H + +#define FENCE_KDUMP_VERSION "0.1" + +static inline void +print_version (const char *self) +{ + fprintf (stdout, "%s %s\n", basename (self), FENCE_KDUMP_VERSION); +} + +#endif /* _FENCE_KDUMP_VERSION_H */ |