summaryrefslogtreecommitdiffstats
path: root/agents/virt/client
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:50:17 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:50:17 +0000
commit86ed03f8adee56c050c73018537371c230a664a6 (patch)
treeeae3d04cdf1c49848e5a671327ab38297f4acb0d /agents/virt/client
parentInitial commit. (diff)
downloadfence-agents-upstream/4.12.1.tar.xz
fence-agents-upstream/4.12.1.zip
Adding upstream version 4.12.1.upstream/4.12.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'agents/virt/client')
-rw-r--r--agents/virt/client/Makefile.am40
-rw-r--r--agents/virt/client/main.c198
-rw-r--r--agents/virt/client/mcast.c393
-rw-r--r--agents/virt/client/options.c1000
-rw-r--r--agents/virt/client/serial.c311
-rw-r--r--agents/virt/client/tcp.c171
-rw-r--r--agents/virt/client/vsock.c176
7 files changed, 2289 insertions, 0 deletions
diff --git a/agents/virt/client/Makefile.am b/agents/virt/client/Makefile.am
new file mode 100644
index 0000000..4a1997b
--- /dev/null
+++ b/agents/virt/client/Makefile.am
@@ -0,0 +1,40 @@
+###############################################################################
+###############################################################################
+##
+## Copyright (C) 2009-2019 Red Hat, Inc.
+##
+## This copyrighted material is made available to anyone wishing to use,
+## modify, copy, or redistribute it subject to the terms and conditions
+## of the GNU General Public License v.2
+##
+###############################################################################
+###############################################################################
+
+MAINTAINERCLEANFILES = Makefile.in
+
+sbin_PROGRAMS = fence_virt
+
+TARGET = $(sbin_PROGRAMS)
+
+fence_virt_SOURCES = mcast.c serial.c main.c options.c tcp.c vsock.c
+
+fence_virt_CFLAGS = $(VIRT_AM_CFLAGS) $(nss_CFLAGS) $(xml2_CFLAGS) $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+
+fence_virt_LDFLAGS = $(VIRT_AM_LDFLAGS) $(COMMON_LDFLAGS)
+fence_virt_LDADD = $(VIRT_COMMON_LIBS) $(nss_LIBS) $(xml2_LIBS) $(PTHREAD_LIBS)
+
+if xvmcompat
+install-exec-hook: fence_virt
+ (cd $(DESTDIR)/${sbindir}; $(LN_S) -nf $^ fence_xvm)
+
+uninstall-hook:
+ (cd $(DESTDIR)/${sbindir}; rm -f fence_xvm)
+endif
+
+fence_virt.delay-check: fence_virt
+ $(eval INPUT=$(subst .delay-check,,$@))
+ test `/usr/bin/time -p ./$(INPUT) -w 10 -n test $(FENCE_TEST_ARGS) -- 2>&1 |\
+ awk -F"[. ]" -vOFS= '/real/ {print $$2,$$3}' | tail -n 1` -ge 1000 || \
+ /usr/bin/time -p ./$(INPUT) -w 0 -n test $(FENCE_TEST_ARGS) --
+
+include $(top_srcdir)/make/agentccheck.mk
diff --git a/agents/virt/client/main.c b/agents/virt/client/main.c
new file mode 100644
index 0000000..188c05d
--- /dev/null
+++ b/agents/virt/client/main.c
@@ -0,0 +1,198 @@
+/*
+ Copyright Red Hat, Inc. 2006
+
+ 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, 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; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <pthread.h>
+#include <libgen.h>
+#include <syslog.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "options.h"
+#include "debug.h"
+#include "client.h"
+
+
+int
+main(int argc, char **argv)
+{
+ fence_virt_args_t args;
+ const char *my_options;
+ int ret = 0;
+
+ args_init(&args);
+ if (!strcmp(basename(argv[0]), "fence_xvm")) {
+ my_options = "di:a:p:r:C:c:k:M:n:H:uo:t:?hVw:";
+ args.mode = MODE_MULTICAST;
+ } else {
+ my_options = "dD:P:A:p:M:n:H:o:t:?hVT:S::C:c:k:w:";
+ args.mode = MODE_SERIAL;
+ }
+
+ if (argc == 1) {
+ args_get_stdin(my_options, &args);
+ } else {
+ args_get_getopt(argc, argv, my_options, &args);
+ }
+
+ if (args.flags & F_HELP) {
+ args_usage(argv[0], my_options, 0);
+
+ printf("With no command line argument, arguments are "
+ "read from standard input.\n");
+ printf("Arguments read from standard input take "
+ "the form of:\n\n");
+ printf(" arg1=value1\n");
+ printf(" arg2=value2\n\n");
+
+ args_usage(argv[0], my_options, 1);
+ exit(0);
+ }
+
+ if (args.flags & F_VERSION) {
+ printf("%s %s\n", basename(argv[0]), XVM_VERSION);
+#ifdef VERSION
+ printf("fence release %s\n", VERSION);
+#endif
+ exit(0);
+ }
+
+ openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON);
+
+ args_finalize(&args);
+ dset(args.debug);
+
+ if (args.debug > 0)
+ args_print(&args);
+
+ /* Additional validation here */
+ if (!args.domain && (args.op != FENCE_DEVSTATUS &&
+ args.op != FENCE_HOSTLIST &&
+ args.op != FENCE_METADATA)) {
+ printf("No domain specified!\n");
+ syslog(LOG_NOTICE, "No domain specified");
+ args.flags |= F_ERR;
+ }
+
+ if (args.net.ipaddr)
+ args.mode = MODE_TCP;
+
+ if (args.net.cid >= 2)
+ args.mode = MODE_VSOCK;
+
+ if (args.flags & F_ERR) {
+ if (args.op != FENCE_VALIDATEALL)
+ args_usage(argv[0], my_options, (argc == 1));
+ exit(1);
+ }
+
+ if (args.op == FENCE_VALIDATEALL)
+ exit(0);
+
+ if (args.op == FENCE_METADATA) {
+ args_metadata(argv[0], my_options);
+ exit(0);
+ }
+
+ if (args.delay > 0 &&
+ args.op != FENCE_STATUS &&
+ args.op != FENCE_DEVSTATUS &&
+ args.op != FENCE_HOSTLIST)
+ sleep(args.delay);
+
+ switch(args.mode) {
+ case MODE_MULTICAST:
+ ret = mcast_fence_virt(&args);
+ break;
+ case MODE_SERIAL:
+ ret = serial_fence_virt(&args);
+ break;
+ case MODE_TCP:
+ ret = tcp_fence_virt(&args);
+ break;
+ case MODE_VSOCK:
+ ret = vsock_fence_virt(&args);
+ break;
+ default:
+ ret = 1;
+ goto out;
+ }
+
+ switch(ret) {
+ case RESP_OFF:
+ if (args.op == FENCE_STATUS)
+ printf("Status: OFF\n");
+ else if (args.domain)
+ syslog(LOG_NOTICE, "Domain \"%s\" is OFF", args.domain);
+ break;
+ case 0:
+ if (args.op == FENCE_STATUS)
+ printf("Status: ON\n");
+ else if (args.domain)
+ syslog(LOG_NOTICE, "Domain \"%s\" is ON", args.domain);
+ break;
+ case RESP_FAIL:
+ if (args.domain) {
+ syslog(LOG_NOTICE, "Fence operation failed for domain \"%s\"",
+ args.domain);
+ } else
+ syslog(LOG_NOTICE, "Fence operation failed");
+ printf("Operation failed\n");
+ break;
+ case RESP_PERM:
+ if (args.domain) {
+ syslog(LOG_NOTICE,
+ "Permission denied for Fence operation for domain \"%s\"",
+ args.domain);
+ } else
+ syslog(LOG_NOTICE, "Permission denied for fence operation");
+ printf("Permission denied\n");
+ break;
+ default:
+ if (args.domain) {
+ syslog(LOG_NOTICE, "Unknown response (%d) for domain \"%s\"",
+ ret, args.domain);
+ } else
+ syslog(LOG_NOTICE, "Unknown response (%d)", ret);
+ printf("Unknown response (%d)\n", ret);
+ break;
+ }
+
+out:
+ closelog();
+ exit(ret);
+}
diff --git a/agents/virt/client/mcast.c b/agents/virt/client/mcast.c
new file mode 100644
index 0000000..42f0a6b
--- /dev/null
+++ b/agents/virt/client/mcast.c
@@ -0,0 +1,393 @@
+/*
+ Copyright Red Hat, Inc. 2006-2017
+
+ 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, 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; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <libgen.h>
+#include <nss.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "ip_lookup.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "tcp.h"
+#include "mcast.h"
+#include "debug.h"
+#include "fdops.h"
+#include "client.h"
+
+
+static int
+tcp_wait_connect(int lfd, int retry_tenths)
+{
+ int fd;
+ fd_set rfds;
+ int n;
+ struct timeval tv;
+
+ dbg_printf(3, "Waiting for connection from XVM host daemon.\n");
+ FD_ZERO(&rfds);
+ FD_SET(lfd, &rfds);
+ tv.tv_sec = retry_tenths / 10;
+ tv.tv_usec = (retry_tenths % 10) * 100000;
+
+ n = select(lfd + 1, &rfds, NULL, NULL, &tv);
+ if (n == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ } else if (n < 0) {
+ return -1;
+ }
+
+ fd = accept(lfd, NULL, 0);
+ if (fd < 0)
+ return -1;
+
+ return fd;
+}
+
+
+void
+do_read_hostlist(int fd, int timeout)
+{
+ host_state_t hinfo;
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ ret = _select_retry(fd+1, &rfds, NULL, NULL, &tv);
+ if (ret == 0) {
+ printf("Timed out!\n");
+ break;
+ }
+
+ ret = _read_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret < sizeof(hinfo)) {
+ printf("Bad read!\n");
+ break;
+ }
+
+ if (strlen((char *)hinfo.uuid) == 0 &&
+ strlen((char *)hinfo.domain) == 0)
+ break;
+
+ printf("%-32s %s %s\n", hinfo.domain, hinfo.uuid,
+ (hinfo.state == 1) ? "on" : "off");
+ } while (1);
+}
+
+
+static int
+tcp_exchange(int fd, fence_auth_type_t auth, void *key,
+ size_t key_len, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ char ret = 1;
+
+ /* Ok, we're connected */
+ dbg_printf(3, "Issuing TCP challenge\n");
+ if (sock_challenge(fd, auth, key, key_len, timeout) <= 0) {
+ /* Challenge failed */
+ printf("Invalid response to challenge\n");
+ return 1;
+ }
+
+ /* Now they'll send us one, so we need to respond here */
+ dbg_printf(3, "Responding to TCP challenge\n");
+ if (sock_response(fd, auth, key, key_len, timeout) <= 0) {
+ printf("Invalid response to challenge\n");
+ return 1;
+ }
+
+ dbg_printf(2, "TCP Exchange + Authentication done... \n");
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ ret = 1;
+ dbg_printf(3, "Waiting for return value from XVM host\n");
+ if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0)
+ return -1;
+
+ /* Read return code */
+ if (_read_retry(fd, &ret, 1, &tv) < 0)
+ ret = 1;
+
+ if (ret == (char)RESP_HOSTLIST) /* hostlist */ {
+ do_read_hostlist(fd, timeout);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+
+static int
+send_multicast_packets(ip_list_t *ipl, fence_virt_args_t *args,
+ uint32_t seqno, void *key, size_t key_len)
+{
+ fence_req_t freq;
+ int mc_sock;
+ ip_addr_t *ipa;
+ struct sockaddr_in tgt4;
+ struct sockaddr_in6 tgt6;
+ struct sockaddr *tgt;
+ socklen_t tgt_len;
+
+ for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) {
+
+ if (ipa->ipa_family != args->net.family) {
+ dbg_printf(2, "Ignoring %s: wrong family\n", ipa->ipa_address);
+ continue;
+ }
+
+ if (args->net.family == PF_INET) {
+ mc_sock = ipv4_send_sk(ipa->ipa_address, args->net.addr,
+ args->net.port,
+ (struct sockaddr *)&tgt4,
+ sizeof(struct sockaddr_in));
+ tgt = (struct sockaddr *)&tgt4;
+ tgt_len = sizeof(tgt4);
+ } else if (args->net.family == PF_INET6) {
+ mc_sock = ipv6_send_sk(ipa->ipa_address, args->net.addr,
+ args->net.port,
+ (struct sockaddr *)&tgt6,
+ sizeof(struct sockaddr_in6));
+ tgt = (struct sockaddr *)&tgt6;
+ tgt_len = sizeof(tgt6);
+ } else {
+ dbg_printf(2, "Unsupported family %d\n", args->net.family);
+ return -1;
+ }
+
+ if (mc_sock < 0)
+ continue;
+
+ /* Build our packet */
+ memset(&freq, 0, sizeof(freq));
+ if (args->domain && strlen((char *)args->domain)) {
+ strncpy((char *)freq.domain, args->domain,
+ sizeof(freq.domain) - 1);
+ }
+ freq.request = args->op;
+ freq.hashtype = args->net.hash;
+ freq.seqno = seqno;
+
+ /* Store source address */
+ if (ipa->ipa_family == PF_INET) {
+ freq.addrlen = sizeof(struct in_addr);
+ /* XXX Swap order for in_addr ? XXX */
+ if (inet_pton(PF_INET, ipa->ipa_address, freq.address) != 1) {
+ dbg_printf(2, "Unable to convert address\n");
+ close(mc_sock);
+ return -1;
+ }
+ } else if (ipa->ipa_family == PF_INET6) {
+ freq.addrlen = sizeof(struct in6_addr);
+ if (inet_pton(PF_INET6, ipa->ipa_address, freq.address) != 1) {
+ dbg_printf(2, "Unable to convert address\n");
+ close(mc_sock);
+ return -1;
+ }
+ }
+
+ freq.flags = 0;
+ if (args->flags & F_USE_UUID)
+ freq.flags |= RF_UUID;
+ freq.family = ipa->ipa_family;
+ freq.port = args->net.port;
+
+ sign_request(&freq, key, key_len);
+
+ dbg_printf(3, "Sending to %s via %s\n", args->net.addr,
+ ipa->ipa_address);
+
+ if(sendto(mc_sock, &freq, sizeof(freq), 0,
+ (struct sockaddr *)tgt, tgt_len) < 0) {
+ dbg_printf(3, "Unable to send packet to %s via %s\n",
+ args->net.addr, ipa->ipa_address);
+ }
+
+ close(mc_sock);
+ }
+
+ return 0;
+}
+
+
+/* TODO: Clean this up!!! */
+int
+mcast_fence_virt(fence_virt_args_t *args)
+{
+ ip_list_t ipl;
+ char key[MAX_KEY_LEN];
+ struct timeval tv;
+ int lfd = -1, key_len = 0, fd, ret;
+ int attempts = 0;
+ uint32_t seqno;
+
+ /* Initialize NSS; required to do hashing, as silly as that
+ sounds... */
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ printf("Could not initialize NSS\n");
+ return 1;
+ }
+
+ if (args->net.auth != AUTH_NONE || args->net.hash != HASH_NONE) {
+ key_len = read_key_file(args->net.key_file, key, sizeof(key));
+ if (key_len < 0) {
+ printf("Could not read %s; trying without "
+ "authentication\n", args->net.key_file);
+ args->net.auth = AUTH_NONE;
+ args->net.hash = HASH_NONE;
+ key_len = 0;
+ }
+ }
+
+ /* Do the real work */
+ if (ip_build_list(&ipl) < 0) {
+ printf("Error building IP address list\n");
+ return 1;
+ }
+
+ attempts = args->timeout * 10 / args->retr_time;
+
+ listen_loop:
+ do {
+ switch (args->net.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ if (args->net.family == PF_INET) {
+ lfd = ipv4_listen(NULL, args->net.port, 10);
+ } else {
+ lfd = ipv6_listen(NULL, args->net.port, 10);
+ }
+ break;
+ /*case AUTH_X509:*/
+ /* XXX Setup SSL listener socket here */
+ default:
+ return 1;
+ }
+
+ if (lfd < 0) {
+ printf("Failed to listen: %s\n", strerror(errno));
+ usleep(args->retr_time * 100000);
+ if (--attempts > 0)
+ goto listen_loop;
+ }
+ } while (0);
+
+ if (lfd < 0)
+ return -1;
+
+ gettimeofday(&tv, NULL);
+ seqno = (uint32_t)tv.tv_usec;
+
+ do {
+ if (send_multicast_packets(&ipl, args, seqno,
+ key, key_len)) {
+ close(lfd);
+ return -1;
+ }
+
+ switch (args->net.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ fd = tcp_wait_connect(lfd, args->retr_time);
+ if (fd < 0 && (errno == ETIMEDOUT ||
+ errno == EINTR))
+ continue;
+ break;
+ /* case AUTH_X509:
+ ... = ssl_wait_connect... */
+ break;
+ default:
+ close(lfd);
+ return 1;
+ }
+
+ break;
+ } while (--attempts);
+
+ if (lfd >= 0)
+ close(lfd);
+
+ if (fd < 0) {
+ if (attempts <= 0) {
+ printf("Timed out waiting for response\n");
+ return 1;
+ }
+ printf("Operation failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ switch (args->net.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ ret = tcp_exchange(fd, args->net.auth, key, key_len,
+ args->timeout);
+ break;
+ /* case AUTH_X509:
+ return ssl_exchange(...); */
+ default:
+ ret = 1;
+ break;
+ }
+
+ close(fd);
+ return ret;
+}
diff --git a/agents/virt/client/options.c b/agents/virt/client/options.c
new file mode 100644
index 0000000..ddd6bc4
--- /dev/null
+++ b/agents/virt/client/options.c
@@ -0,0 +1,1000 @@
+/*
+ Copyright Red Hat, Inc. 2006
+
+ 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, 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; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libgen.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "mcast.h"
+#include "tcp_listener.h"
+#include "options.h"
+
+#define SCHEMA_COMPAT '\xfe'
+
+
+/* Assignment functions */
+
+static inline void
+assign_debug(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value) {
+ /* GNU getopt sets optarg to NULL for options w/o a param
+ We rely on this here... */
+ args->debug++;
+ return;
+ }
+
+ args->debug = atoi(value);
+ if (args->debug < 0) {
+ args->debug = 1;
+ }
+}
+
+
+static inline void
+assign_family(fence_virt_args_t *args, struct arg_info *arg,
+ char *value)
+{
+ if (!value)
+ return;
+
+ if (!strcasecmp(value, "ipv4")) {
+ args->net.family = PF_INET;
+ } else if (!strcasecmp(value, "ipv6")) {
+ args->net.family = PF_INET6;
+ } else if (!strcasecmp(value, "auto")) {
+ args->net.family = 0;
+ } else {
+ printf("Unsupported family: '%s'\n", value);
+ args->flags |= F_ERR;
+ }
+}
+
+
+static inline void
+assign_address(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ if (args->net.addr)
+ free(args->net.addr);
+ args->net.addr = strdup(value);
+}
+
+static inline void
+assign_ip_address(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ if (args->net.ipaddr)
+ free(args->net.ipaddr);
+ args->net.ipaddr = strdup(value);
+}
+
+static inline void
+assign_channel_address(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ if (args->serial.address)
+ free(args->serial.address);
+ args->serial.address = strdup(value);
+}
+
+
+static inline void
+assign_port(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ char *p;
+ int ret;
+
+ if (!value)
+ return;
+
+ ret = strtol(value, &p, 0);
+ if (ret <= 0 || ret >= 65536 || *p != '\0') {
+ printf("Invalid port: '%s'\n", value);
+ args->flags |= F_ERR;
+ } else
+ args->net.port = ret;
+}
+
+static inline void
+assign_cid(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ char *p;
+ unsigned long ret;
+
+ if (!value) {
+ args->net.cid = 2;
+ return;
+ }
+
+ ret = strtoul(value, &p, 0);
+ if (!p || *p != '\0' || ret < 2 || ret >= 0xffffffff) {
+ printf("Invalid CID: '%s'\n", value);
+ args->flags |= F_ERR;
+ } else
+ args->net.cid = ret;
+}
+
+static inline void
+assign_interface(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ int ret;
+
+ if (!value)
+ return;
+
+ ret = if_nametoindex(value);
+ if (ret <= 0) {
+ printf("Invalid interface: %s: %s\n", value, strerror(errno));
+ args->net.ifindex = 0;
+ }
+
+ args->net.ifindex = ret;
+}
+
+
+static inline void
+assign_retrans(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ char *p;
+ int ret;
+
+ if (!value)
+ return;
+
+ ret = strtol(value, &p, 0);
+ if (ret <= 0 || *p != '\0') {
+ printf("Invalid retransmit time: '%s'\n", value);
+ args->flags |= F_ERR;
+ } else
+ args->retr_time = ret;
+}
+
+static inline void
+assign_hash(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ if (!strcasecmp(value, "none")) {
+ args->net.hash = HASH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->net.hash = HASH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->net.hash = HASH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->net.hash = HASH_SHA512;
+ } else {
+ printf("Unsupported hash: %s\n", value);
+ args->flags |= F_ERR;
+ }
+}
+
+
+static inline void
+assign_auth(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ if (!strcasecmp(value, "none")) {
+ args->net.auth = AUTH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->net.auth = AUTH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->net.auth = AUTH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->net.auth = AUTH_SHA512;
+ } else {
+ printf("Unsupported auth type: %s\n", value);
+ args->flags |= F_ERR;
+ }
+}
+
+static inline void
+assign_key(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ struct stat st;
+
+ if (!value)
+ return;
+
+ if (args->net.key_file)
+ free(args->net.key_file);
+ args->net.key_file = strdup(value);
+
+ if (stat(value, &st) == -1) {
+ printf("Invalid key file: '%s' (%s)\n", value,
+ strerror(errno));
+ args->flags |= F_ERR;
+ }
+}
+
+
+static inline void
+assign_op(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ if (!strcasecmp(value, "null")) {
+ args->op = FENCE_NULL;
+ } else if (!strcasecmp(value, "on")) {
+ args->op = FENCE_ON;
+ } else if (!strcasecmp(value, "off")) {
+ args->op = FENCE_OFF;
+ } else if (!strcasecmp(value, "reboot")) {
+ args->op = FENCE_REBOOT;
+ } else if (!strcasecmp(value, "status")) {
+ args->op = FENCE_STATUS;
+ } else if (!strcasecmp(value, "monitor")) {
+ args->op = FENCE_DEVSTATUS;
+ } else if (!strcasecmp(value, "list") || !strcasecmp(value, "list-status")) {
+ args->op = FENCE_HOSTLIST;
+ } else if (!strcasecmp(value, "metadata")) {
+ args->op = FENCE_METADATA;
+ } else if (!strcasecmp(value, "validate-all")) {
+ args->op = FENCE_VALIDATEALL;
+ } else {
+ printf("Unsupported operation: %s\n", value);
+ args->flags |= F_ERR;
+ }
+}
+
+
+static inline void
+assign_device(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ args->serial.device = strdup(value);
+}
+
+
+static inline void
+assign_params(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value)
+ return;
+
+ args->serial.speed = strdup(value);
+}
+
+
+static inline void
+assign_domain(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (args->domain) {
+ printf("Domain/UUID may not be specified more than once\n");
+ args->flags |= F_ERR;
+ return;
+ }
+
+ if (!value)
+ return;
+
+ args->domain = strdup(value);
+
+ if (strlen(value) <= 0) {
+ printf("Invalid domain name\n");
+ args->flags |= F_ERR;
+ }
+
+ if (strlen(value) >= MAX_DOMAINNAME_LENGTH) {
+ errno = ENAMETOOLONG;
+ printf("Invalid domain name: '%s' (%s)\n",
+ value, strerror(errno));
+ args->flags |= F_ERR;
+ }
+}
+
+
+static inline void
+assign_uuid_lookup(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ if (!value) {
+ /* GNU getopt sets optarg to NULL for options w/o a param
+ We rely on this here... */
+ args->flags |= F_USE_UUID;
+ return;
+ }
+
+ args->flags |= ( !!atoi(value) ? F_USE_UUID : 0);
+}
+
+
+static inline void
+assign_timeout(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ char *p;
+ int ret;
+
+ if (!value)
+ return;
+
+ ret = strtol(value, &p, 0);
+ if (ret <= 0 || *p != '\0') {
+ printf("Invalid timeout: '%s'\n", value);
+ args->flags |= F_ERR;
+ } else
+ args->timeout = ret;
+}
+
+static inline void
+assign_delay(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ char *p;
+ int ret;
+
+ if (!value)
+ return;
+
+ ret = strtol(value, &p, 0);
+ if (ret < 0 || *p != '\0') {
+ printf("Invalid delay: '%s'\n", value);
+ args->flags |= F_ERR;
+ } else
+ args->delay = ret;
+}
+
+static inline void
+assign_help(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ args->flags |= F_HELP;
+}
+
+
+static inline void
+assign_version(fence_virt_args_t *args, struct arg_info *arg, char *value)
+{
+ args->flags |= F_VERSION;
+}
+
+
+static void
+print_desc_xml(const char *desc)
+{
+ const char *d;
+
+ for (d = desc; *d; d++) {
+ switch (*d) {
+ case '<':
+ printf("&lt;");
+ break;
+ case '>':
+ printf("&gt;");
+ break;
+ default:
+ printf("%c", *d);
+ }
+ }
+}
+
+
+/** ALL valid command line and stdin arguments for this fencing agent */
+static struct arg_info _arg_info[] = {
+ { '\xff', NULL, "agent",
+ NULL, 0, 0, "string", NULL,
+ "Not user serviceable",
+ NULL },
+
+ { '\xff', NULL, "self",
+ NULL, 0, 0, "string", NULL,
+ "Not user serviceable",
+ NULL },
+
+ { '\xff', NULL, "nodename",
+ NULL, 0, 0, "string", NULL,
+ "Not user serviceable",
+ NULL },
+
+ { 'd', "-d", "debug",
+ NULL, 0, 0, "boolean", NULL,
+ "Specify (stdin) or increment (command line) debug level",
+ assign_debug },
+
+ { 'i', "-i <family>", "ip_family",
+ NULL, 0, 0, "string", "auto",
+ "IP Family ([auto], ipv4, ipv6)",
+ assign_family },
+
+ { 'a', "-a <address>", "multicast_address",
+ NULL, 0, 0, "string", NULL,
+ "Multicast address (default=" IPV4_MCAST_DEFAULT " / " IPV6_MCAST_DEFAULT ")",
+ assign_address },
+
+ { 'T', "-T <address>", "ipaddr",
+ NULL, 0, 0, "string", "127.0.0.1",
+ "IP address to connect to in TCP mode (default=" IPV4_TCP_ADDR_DEFAULT " / " IPV6_TCP_ADDR_DEFAULT ")",
+ assign_ip_address },
+
+ { 'S', "-S <cid>", "vsock",
+ NULL, 0, 0, "integer", "2",
+ "vm socket CID to connect to in vsock mode",
+ assign_cid },
+
+ { 'A', "-A <address>", "channel_address",
+ NULL, 0, 0, "string", "10.0.2.179",
+ "VM Channel IP address (default=" DEFAULT_CHANNEL_IP ")",
+ assign_channel_address },
+
+ { 'p', "-p <port>", "ipport",
+ NULL, 0, 0, "string", "1229",
+ "TCP, Multicast, VMChannel, or VM socket port (default=1229)",
+ assign_port },
+
+ { 'I', "-I <interface>", "interface",
+ NULL, 0, 0, "string", NULL,
+ "Network interface name to listen on",
+ assign_interface },
+
+ { 'r', "-r <retrans>", "retrans",
+ NULL, 0, 0, "string", "20",
+ "Multicast retransmit time (in 1/10sec; default=20)",
+ assign_retrans },
+
+ { 'c', "-c <hash>", "hash",
+ NULL, 0, 0, "string", "sha256",
+ "Packet hash strength (none, sha1, [sha256], sha512)",
+ assign_hash },
+
+ { 'C', "-C <auth>", "auth",
+ NULL, 0, 0, "string", "sha256",
+ "Authentication (none, sha1, [sha256], sha512)",
+ assign_auth },
+
+ { 'k', "-k <file>", "key_file",
+ NULL, 0, 0, "string", DEFAULT_KEY_FILE,
+ "Shared key file (default=" DEFAULT_KEY_FILE ")",
+ assign_key },
+
+ { 'D', "-D <device>", "serial_device",
+ NULL, 0, 0, "string", DEFAULT_SERIAL_DEVICE,
+ "Serial device (default=" DEFAULT_SERIAL_DEVICE ")",
+ assign_device },
+
+ { 'P', "-P <param>", "serial_params",
+ NULL, 0, 0, "string", DEFAULT_SERIAL_SPEED,
+ "Serial Parameters (default=" DEFAULT_SERIAL_SPEED ")",
+ assign_params },
+
+ { '\xff', NULL, "option",
+ /* Deprecated */
+ NULL, 0, 0, "string", "reboot",
+ "Fencing option (null, off, on, [reboot], status, list, list-status, monitor, validate-all, metadata)",
+ assign_op },
+
+ { 'o', "-o <operation>", "action",
+ NULL, 0, 0, "string", "reboot",
+ "Fencing action (null, off, on, [reboot], status, list, list-status, monitor, validate-all, metadata)",
+ assign_op },
+
+ { 'n', "-n <domain>", "plug",
+ "port", 0, 0, "string", NULL,
+ "Virtual Machine (domain name) to fence",
+ assign_domain },
+
+ { 'H', "-H <domain>", "port",
+ NULL, 1, 0, "string", NULL,
+ "Virtual Machine (domain name) to fence",
+ assign_domain },
+
+ { SCHEMA_COMPAT, NULL, "domain",
+ NULL, 0, 0, "string", NULL,
+ "Virtual Machine (domain name) to fence (deprecated; use port)",
+ assign_domain },
+
+ { 'u', "-u", "use_uuid",
+ NULL, 0, 0, "string", "0",
+ "Treat [domain] as UUID instead of domain name. This is provided for compatibility with older fence_xvmd installations.",
+ assign_uuid_lookup },
+
+ { 't', "-t <timeout>", "timeout",
+ NULL, 0, 0, "string", "30",
+ "Fencing timeout (in seconds; default=30)",
+ assign_timeout },
+
+ { 'h', "-h", NULL,
+ NULL, 0, 0, "boolean", "0",
+ "Help",
+ assign_help },
+
+ { '?', "-?", NULL,
+ NULL, 0, 0, "boolean", "0",
+ "Help (alternate)",
+ assign_help },
+
+ { 'w', "-w <delay>", "delay",
+ NULL, 0, 0, "string", "0",
+ "Fencing delay (in seconds; default=0)",
+ assign_delay },
+
+ { 'V', "-V", NULL,
+ NULL, 0, 0, "boolean", "0",
+ "Display version and exit",
+ assign_version },
+
+ /* Terminator */
+ { 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL }
+};
+
+
+static struct arg_info *
+find_arg_by_char(char arg)
+{
+ int x = 0;
+
+ for (x = 0; _arg_info[x].opt != 0; x++) {
+ if (_arg_info[x].opt == arg)
+ return &_arg_info[x];
+ }
+
+ return NULL;
+}
+
+
+static struct arg_info *
+find_arg_by_string(char *arg)
+{
+ int x = 0;
+
+ for (x = 0; _arg_info[x].opt != 0; x++) {
+ if (!_arg_info[x].stdin_opt)
+ continue;
+ if (!strcasecmp(_arg_info[x].stdin_opt, arg))
+ return &_arg_info[x];
+ }
+
+ return NULL;
+}
+
+
+/* ============================================================= */
+
+/**
+ Initialize an args structure.
+
+ @param args Pointer to args structure to initialize.
+ */
+void
+args_init(fence_virt_args_t *args)
+{
+ args->domain = NULL;
+ //args->uri = NULL;
+ args->op = FENCE_REBOOT;
+ args->net.key_file = strdup(DEFAULT_KEY_FILE);
+ args->net.hash = DEFAULT_HASH;
+ args->net.auth = DEFAULT_AUTH;
+ args->net.addr = NULL;
+ args->net.ipaddr = NULL;
+ args->net.cid = 0;
+ args->net.port = DEFAULT_MCAST_PORT;
+ args->net.ifindex = 0;
+ args->net.family = 0; /* auto */
+ args->serial.device = NULL;
+ args->serial.speed = strdup(DEFAULT_SERIAL_SPEED);
+ args->serial.address = strdup(DEFAULT_CHANNEL_IP);
+ args->timeout = 30;
+ args->retr_time = 20;
+ args->flags = 0;
+ args->debug = 0;
+ args->delay = 0;
+}
+
+
+#define _pr_int(piece) printf(" %s = %d\n", #piece, piece)
+#define _pr_str(piece) printf(" %s = %s\n", #piece, piece)
+
+
+/**
+ Prints out the contents of an args structure for debugging.
+
+ @param args Pointer to args structure to print out.
+ */
+void
+args_print(fence_virt_args_t *args)
+{
+ printf("-- args @ %p --\n", args);
+ _pr_str(args->domain);
+ _pr_int(args->op);
+ _pr_int(args->mode);
+ _pr_int(args->debug);
+ _pr_int(args->timeout);
+ _pr_int(args->delay);
+ _pr_int(args->retr_time);
+ _pr_int(args->flags);
+
+ _pr_str(args->net.addr);
+ _pr_str(args->net.ipaddr);
+ _pr_int(args->net.cid);
+ _pr_str(args->net.key_file);
+ _pr_int(args->net.port);
+ _pr_int(args->net.hash);
+ _pr_int(args->net.auth);
+ _pr_int(args->net.family);
+ _pr_int(args->net.ifindex);
+
+ _pr_str(args->serial.device);
+ _pr_str(args->serial.speed);
+ _pr_str(args->serial.address);
+ printf("-- end args --\n");
+}
+
+
+/**
+ Print out arguments and help information based on what is allowed in
+ the getopt string optstr.
+
+ @param progname Program name.
+ @param optstr Getopt(3) style options string
+ @param print_stdin 0 = print command line options + description,
+ 1 = print fence-style stdin args + description
+ */
+static char *
+find_rev(const char *start, char *curr, char c)
+{
+
+ while (curr > start) {
+ if (*curr == c)
+ return curr;
+ --curr;
+ }
+
+ return NULL;
+}
+
+
+static void
+output_help_text(int arg_width, int help_width, const char *arg, const char *desc)
+{
+ char out_buf[4096];
+ char *p, *start;
+ const char *arg_print = arg;
+ int len;
+
+ memset(out_buf, 0, sizeof(out_buf));
+ strncpy(out_buf, desc, sizeof(out_buf) - 1);
+ start = out_buf;
+
+ do {
+ p = NULL;
+ len = strlen(start);
+ if (len > help_width) {
+ p = start + help_width;
+ p = find_rev(start, p, ' ');
+ if (p) {
+ *p = 0;
+ p++;
+ }
+ }
+ printf(" %*.*s %*.*s\n",
+ -arg_width, arg_width,
+ arg_print,
+ -help_width, help_width,
+ start);
+ if (!p)
+ return;
+ if (arg == arg_print)
+ arg_print = " ";
+ start = p;
+ } while(1);
+}
+
+
+void
+args_usage(char *progname, const char *optstr, int print_stdin)
+{
+ int x;
+ struct arg_info *arg;
+
+ if (!print_stdin) {
+ if (progname) {
+ printf("usage: %s [args]\n\nNOTE: reboot-action does not power on nodes that are powered off.\n\n", progname);
+ } else {
+ printf("usage: fence_virt [args]\n\nNOTE: reboot-action does not power on nodes that are powered off.\n\n");
+ }
+ }
+
+ for (x = 0; x < strlen(optstr); x++) {
+ arg = find_arg_by_char(optstr[x]);
+ if (!arg || arg->deprecated)
+ continue;
+
+ if (print_stdin) {
+ if (arg && arg->stdin_opt)
+ output_help_text(20, 55, arg->stdin_opt, arg->desc);
+ } else {
+ output_help_text(20, 55, arg->opt_desc, arg->desc);
+ }
+ }
+
+ printf("\n");
+}
+
+
+void
+args_metadata(char *progname, const char *optstr)
+{
+ int x;
+ struct arg_info *arg;
+
+ printf("<?xml version=\"1.0\" ?>\n");
+ printf("<resource-agent name=\"%s\" shortdesc=\"Fence agent for virtual machines\">\n", basename(progname));
+ printf("<longdesc>%s is an I/O Fencing agent which can be used with "
+ "virtual machines.\n\nNOTE: reboot-action does not power on nodes that are powered off."
+ "</longdesc>\n", basename(progname));
+ printf("<vendor-url>https://libvirt.org</vendor-url>\n");
+ printf("<parameters>\n");
+
+ for (x = 0; x < strlen(optstr); x++) {
+ arg = find_arg_by_char(optstr[x]);
+ if (!arg)
+ continue;
+ if (!arg->stdin_opt)
+ continue;
+
+ if (arg->obsoletes)
+ printf("\t<parameter name=\"%s\" unique=\"0\" required=\"%d\" obsoletes=\"%s\">\n", arg->stdin_opt, (!strcmp(arg->content_type, "boolean") || arg->default_value) ? 0 : 1, arg->obsoletes);
+ else if (arg->deprecated)
+ printf("\t<parameter name=\"%s\" unique=\"0\" required=\"%d\" deprecated=\"%d\">\n", arg->stdin_opt, (!strcmp(arg->content_type, "boolean") || arg->default_value) ? 0 : 1, arg->deprecated);
+ else
+ printf("\t<parameter name=\"%s\" unique=\"0\" required=\"%d\">\n", arg->stdin_opt, (!strcmp(arg->content_type, "boolean") || arg->default_value || !strcmp(arg->stdin_opt, "multicast_address")) ? 0 : 1);
+
+ printf("\t\t<getopt mixed=\"-%c\" />\n",arg->opt);
+ if (arg->default_value) {
+ printf("\t\t<content type=\"%s\" default=\"%s\" />\n", arg->content_type, arg->default_value);
+ } else {
+ printf("\t\t<content type=\"%s\" />\n", arg->content_type);
+ }
+ printf("\t\t<shortdesc lang=\"en\">");
+ print_desc_xml(arg->desc);
+ printf("</shortdesc>\n");
+ printf("\t</parameter>\n");
+ }
+
+ for (x = 0; _arg_info[x].opt != 0; x++) {
+ if (_arg_info[x].opt != SCHEMA_COMPAT)
+ continue;
+
+ arg = &_arg_info[x];
+
+ printf("\t<parameter name=\"%s\" unique=\"0\" required=\"%d\" deprecated=\"1\">\n", arg->stdin_opt,
+ (!strcmp(arg->content_type, "boolean") || arg->default_value || !strcmp(arg->stdin_opt, "domain")) ? 0 : 1);
+ printf("\t\t<getopt mixed=\"\" />\n");
+ if (arg->default_value) {
+ printf("\t\t<content type=\"%s\" default=\"%s\" />\n", arg->content_type, arg->default_value);
+ } else {
+ printf("\t\t<content type=\"%s\" />\n", arg->content_type);
+ }
+ printf("\t\t<shortdesc lang=\"en\">");
+ print_desc_xml(arg->desc);
+ printf("</shortdesc>\n");
+ printf("\t</parameter>\n");
+ }
+
+ printf("</parameters>\n");
+ printf("<actions>\n");
+ printf("\t<action name=\"null\" />\n");
+ printf("\t<action name=\"on\" />\n");
+ printf("\t<action name=\"off\" />\n");
+ printf("\t<action name=\"reboot\" />\n");
+ printf("\t<action name=\"metadata\" />\n");
+ printf("\t<action name=\"status\" />\n");
+ printf("\t<action name=\"monitor\" />\n");
+ printf("\t<action name=\"list\" />\n");
+ printf("\t<action name=\"list-status\" />\n");
+ printf("\t<action name=\"validate-all\" />\n");
+ printf("</actions>\n");
+ printf("</resource-agent>\n");
+}
+
+
+/**
+ Remove leading and trailing whitespace from a line of text.
+
+ @param line Line to clean up
+ @param linelen Max size of line
+ @return 0 on success, -1 on failure
+ */
+static int
+cleanup(char *line, size_t linelen)
+{
+ char *p;
+ int x;
+
+ /* Remove leading whitespace. */
+ p = line;
+ for (x = 0; x < linelen; x++) {
+ switch (line[x]) {
+ case '\t':
+ case ' ':
+ break;
+ case '\n':
+ case '\r':
+ return -1;
+ default:
+ goto eol;
+ }
+ }
+eol:
+ /* Move the remainder down by as many whitespace chars as we
+ chewed up */
+ if (x)
+ memmove(p, &line[x], linelen-x);
+
+ /* Remove trailing whitespace. */
+ for (x=0; x < linelen; x++) {
+ switch(line[x]) {
+ case '\t':
+ case ' ':
+ case '\r':
+ case '\n':
+ line[x] = 0;
+ case 0:
+ /* End of line */
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ Parse args from stdin and assign to the specified args structure.
+
+ @param optstr Command line option string in getopt(3) format
+ @param args Args structure to fill in.
+ */
+void
+args_get_stdin(const char *optstr, fence_virt_args_t *args)
+{
+ char in[256];
+ char *name, *val;
+ struct arg_info *arg;
+
+ while (fgets(in, sizeof(in), stdin)) {
+
+ if (in[0] == '#')
+ continue;
+
+ if (cleanup(in, sizeof(in)) == -1)
+ continue;
+
+ name = in;
+ if ((val = strchr(in, '='))) {
+ *val = 0;
+ ++val;
+ }
+
+ arg = find_arg_by_string(name);
+ if (!arg || (arg->opt != '\xff' &&
+ arg->opt != SCHEMA_COMPAT &&
+ !strchr(optstr, arg->opt))) {
+ fprintf(stderr,
+ "Parse error: Ignoring unknown option '%s'\n",
+ name);
+ continue;
+ }
+
+ if (arg->assign)
+ arg->assign(args, arg, val);
+ }
+}
+
+
+/**
+ Parse args from stdin and assign to the specified args structure.
+
+ @param optstr Command line option string in getopt(3) format
+ @param args Args structure to fill in.
+ */
+void
+args_get_getopt(int argc, char **argv, const char *optstr, fence_virt_args_t *args)
+{
+ int opt;
+ struct arg_info *arg;
+
+ while ((opt = getopt(argc, argv, optstr)) != EOF) {
+
+ arg = find_arg_by_char(opt);
+
+ if (!arg) {
+ args->flags |= F_ERR;
+ continue;
+ }
+
+ if (arg->assign)
+ arg->assign(args, arg, optarg);
+ }
+}
+
+
+void
+args_finalize(fence_virt_args_t *args)
+{
+ char *addr = NULL;
+
+ if (!args->net.addr) {
+ switch(args->net.family) {
+ case 0:
+ case PF_INET:
+ addr = (char *)IPV4_MCAST_DEFAULT;
+ break;
+ case PF_INET6:
+ addr = (char *)IPV6_MCAST_DEFAULT;
+ break;
+ default:
+ args->flags |= F_ERR;
+ break;
+ }
+ }
+
+ if (!args->net.addr)
+ args->net.addr = addr;
+
+ if (!args->net.addr) {
+ printf("No multicast address available\n");
+ args->flags |= F_ERR;
+ }
+
+ if (!args->net.addr)
+ return;
+ if (args->net.family)
+ return;
+
+ /* Set family */
+ if (strchr(args->net.addr, ':'))
+ args->net.family = PF_INET6;
+ if (strchr(args->net.addr, '.'))
+ args->net.family = PF_INET;
+ if (!args->net.family) {
+ printf("Could not determine address family\n");
+ args->flags |= F_ERR;
+ }
+}
diff --git a/agents/virt/client/serial.c b/agents/virt/client/serial.c
new file mode 100644
index 0000000..238ef4a
--- /dev/null
+++ b/agents/virt/client/serial.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2002-2003, 2009 Red Hat, Inc.
+ *
+ * License: GPLv2+
+ *
+ * Written by Lon Hohberger <lhh@redhat.com>
+ *
+ * Serial client for fence_virt (incomplete, but
+ * a good start)
+ *
+ * Based on:
+ * Ubersimpledumbterminal "ser" version 1.0.3
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include "fdops.h"
+#include "xvm.h"
+#include "options.h"
+#include "client.h"
+#include "tcp.h"
+
+
+static int
+char_to_speed(const char *speed)
+{
+ if (!speed || !strlen(speed))
+ return B9600;
+ if (!strcmp(speed,"2400"))
+ return B2400;
+ if (!strcmp(speed,"9600"))
+ return B9600;
+ if (!strcmp(speed,"19200"))
+ return B19200;
+ if (!strcmp(speed,"38400"))
+ return B38400;
+ if (!strcmp(speed,"57600"))
+ return B57600;
+ if (!strcmp(speed,"115200"))
+ return B115200;
+ return -1;
+}
+
+
+static int
+char_to_flags(const char *param)
+{
+ int db_f = CS8, par_f = 0, sb_f = 0, x;
+
+ if (!param || !strlen(param))
+ return (db_f | par_f | sb_f);
+
+ if (strlen(param) < 3) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (x = 0; x < 3; x++) {
+ switch (param[x]) {
+ case '5':
+ db_f = CS5;
+ break;
+ case '6':
+ db_f = CS6;
+ break;
+ case '7':
+ db_f = CS7;
+ break;
+ case '8':
+ db_f = CS8;
+ break;
+ case 'n':
+ case 'N':
+ par_f = 0;
+ break;
+ case 'e':
+ case 'E':
+ par_f = PARENB;
+ break;
+ case 'o':
+ case 'O':
+ par_f = PARENB | PARODD;
+ break;
+ case '1':
+ sb_f = 0;
+ break;
+ case '2':
+ sb_f = CSTOPB;
+ break;
+ default:
+ printf("Fail: %c\n", param[x]);
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ return (db_f | par_f | sb_f);
+}
+
+
+static int
+open_port(char *file, char *cspeed, char *cparam)
+{
+ struct termios ti;
+ int fd, speed = B115200, flags = 0;
+ struct flock lock;
+
+ if ((speed = char_to_speed(cspeed)) == -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((flags = char_to_flags(cparam)) == -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((fd = open(file, O_RDWR | O_EXCL)) == -1) {
+ perror("open");
+ return -1;
+ }
+
+ memset(&lock,0,sizeof(lock));
+ lock.l_type = F_WRLCK;
+ if (fcntl(fd, F_SETLK, &lock) == -1) {
+ perror("Failed to lock serial port");
+ close(fd);
+ return -1;
+ }
+
+ memset(&ti, 0, sizeof(ti));
+ ti.c_cflag = (speed | CLOCAL | CRTSCTS | CREAD | flags);
+
+ if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+ perror("tcsetattr");
+ close(fd);
+ return -1;
+ }
+
+ (void) tcflush(fd, TCIOFLUSH);
+
+ return fd;
+}
+
+
+static void
+hangup(int fd, int delay)
+{
+ unsigned int bits;
+
+ if (ioctl(fd, TIOCMGET, &bits)) {
+ perror("ioctl1");
+ return;
+ }
+
+ bits &= ~(TIOCM_DTR | TIOCM_CTS | TIOCM_RTS | TIOCM_DSR | TIOCM_CD);
+
+ if (ioctl(fd, TIOCMSET, &bits)) {
+ perror("ioctl2");
+ return;
+ }
+
+ usleep(delay);
+
+ bits |= (TIOCM_DTR | TIOCM_CTS | TIOCM_RTS | TIOCM_DSR | TIOCM_CD);
+
+ if (ioctl(fd, TIOCMSET, &bits)) {
+ perror("ioctl3");
+ return;
+ }
+}
+
+static int
+wait_for(int fd, const char *pattern, size_t size, struct timeval *tout)
+{
+ char *pos = (char *)pattern;
+ char c;
+ int n;
+ struct timeval tv;
+ size_t remain = size;
+
+ if (tout) {
+ memcpy(&tv, tout, sizeof(tv));
+ tout = &tv;
+ }
+
+ while (remain) {
+ n = _read_retry(fd, &c, 1, &tv);
+ if (n < 1)
+ return -1;
+
+ if (c == *pos) {
+ ++pos;
+ --remain;
+ } else {
+ pos = (char *)pattern;
+ remain = size;
+ }
+ }
+
+ return 0;
+}
+
+int
+serial_fence_virt(fence_virt_args_t *args)
+{
+ struct in_addr ina;
+ struct in6_addr in6a;
+ serial_req_t req;
+ int fd, ret;
+ char speed[32], *flags = NULL;
+ struct timeval tv;
+ serial_resp_t resp;
+
+ if (args->serial.device) {
+ strncpy(speed, args->serial.speed, sizeof(speed) - 1);
+
+ //printf("Port: %s Speed: %s\n", args->serial.device, speed);
+
+ if ((flags = strchr(speed, ','))) {
+ *flags = 0;
+ flags++;
+ }
+
+ fd = open_port(args->serial.device, speed, flags);
+ if (fd == -1) {
+ perror("open_port");
+ return -1;
+ }
+
+ hangup(fd, 300000);
+ } else {
+ fd = -1;
+ if (inet_pton(PF_INET, args->serial.address, &ina)) {
+ fd = ipv4_connect(&ina, args->net.port, 3);
+ } else if (inet_pton(PF_INET6, args->serial.address, &in6a)) {
+ fd = ipv6_connect(&in6a, args->net.port, 3);
+ }
+
+ if (fd < 0) {
+ perror("vmchannel connect");
+ printf("Failed to connect to %s:%d\n", args->serial.address,
+ args->net.port);
+ return -1;
+ }
+ }
+
+
+ memset(&req, 0, sizeof(req));
+ req.magic = SERIAL_MAGIC;
+ req.request = (uint8_t)args->op;
+ gettimeofday(&tv, NULL);
+ req.seqno = (int)tv.tv_usec;
+
+ if (args->domain)
+ strncpy((char *)req.domain, args->domain, sizeof(req.domain) - 1);
+
+ tv.tv_sec = 3;
+ tv.tv_usec = 0;
+ swab_serial_req_t(&req);
+ ret = _write_retry(fd, &req, sizeof(req), &tv);
+ if (ret < sizeof(req)) {
+ if (ret < 0) {
+ close(fd);
+ return ret;
+ }
+ printf("Failed to send request\n");
+ }
+
+ tv.tv_sec = args->timeout;
+ tv.tv_usec = 0;
+ resp.magic = SERIAL_MAGIC;
+ do {
+ if (wait_for(fd, (const char *)&resp.magic,
+ sizeof(resp.magic), &tv) == 0) {
+ ret = _read_retry(fd, &resp.response, sizeof(resp.response), &tv);
+ } else {
+ /* The other end died or closed the connection */
+ close(fd);
+ return -1;
+ }
+
+ swab_serial_resp_t(&resp);
+ } while(resp.magic != SERIAL_MAGIC && (tv.tv_sec || tv.tv_usec));
+
+ if (resp.magic != SERIAL_MAGIC) {
+ close(fd);
+ return -1;
+ }
+ ret = resp.response;
+ if (resp.response == RESP_HOSTLIST) /* hostlist */ {
+ /* ok read hostlist */
+ do_read_hostlist(fd, args->timeout);
+ ret = 0;
+ }
+
+ close(fd);
+ return ret;
+}
diff --git a/agents/virt/client/tcp.c b/agents/virt/client/tcp.c
new file mode 100644
index 0000000..986fdd9
--- /dev/null
+++ b/agents/virt/client/tcp.c
@@ -0,0 +1,171 @@
+/*
+ Copyright Red Hat, Inc. 2006-2012
+
+ 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, 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; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <nss.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "tcp.h"
+#include "debug.h"
+#include "fdops.h"
+#include "client.h"
+
+static int
+tcp_exchange(int fd, fence_auth_type_t auth, void *key,
+ size_t key_len, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ char ret = 1;
+
+ /* Ok, we're connected */
+ dbg_printf(3, "Issuing TCP challenge\n");
+ if (sock_challenge(fd, auth, key, key_len, timeout) <= 0) {
+ /* Challenge failed */
+ printf("Invalid response to challenge\n");
+ return 1;
+ }
+
+ /* Now they'll send us one, so we need to respond here */
+ dbg_printf(3, "Responding to TCP challenge\n");
+ if (sock_response(fd, auth, key, key_len, timeout) <= 0) {
+ printf("Invalid response to challenge\n");
+ return 1;
+ }
+
+ dbg_printf(2, "TCP Exchange + Authentication done... \n");
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ ret = 1;
+ dbg_printf(3, "Waiting for return value from fence_virtd host\n");
+ if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0)
+ return -1;
+
+ /* Read return code */
+ if (_read_retry(fd, &ret, 1, &tv) < 0)
+ ret = 1;
+
+ if (ret == (char)RESP_HOSTLIST) /* hostlist */ {
+ do_read_hostlist(fd, timeout);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+int
+tcp_fence_virt(fence_virt_args_t *args)
+{
+ char key[MAX_KEY_LEN];
+ struct timeval tv;
+ int key_len = 0, fd = -1;
+ int ret;
+ struct in_addr ina;
+ struct in6_addr in6a;
+ fence_req_t freq;
+
+ /* Initialize NSS; required to do hashing, as silly as that
+ sounds... */
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ printf("Could not initialize NSS\n");
+ return 1;
+ }
+
+ if (args->net.auth != AUTH_NONE || args->net.hash != HASH_NONE) {
+ key_len = read_key_file(args->net.key_file, key, sizeof(key));
+ if (key_len < 0) {
+ printf("Could not read %s; trying without "
+ "authentication\n", args->net.key_file);
+ args->net.auth = AUTH_NONE;
+ args->net.hash = HASH_NONE;
+ key_len = 0;
+ }
+ }
+
+ /* Same wire protocol as fence_xvm */
+ memset(&freq, 0, sizeof(freq));
+ if (args->domain && strlen((char *)args->domain))
+ strncpy((char *)freq.domain, args->domain, sizeof(freq.domain) - 1);
+ freq.request = args->op;
+ freq.hashtype = args->net.hash;
+ freq.flags = 0;
+ if (args->flags & F_USE_UUID)
+ freq.flags |= RF_UUID;
+ gettimeofday(&tv, NULL);
+ freq.seqno = (uint32_t) tv.tv_usec;
+ sign_request(&freq, key, key_len);
+
+ /* XXX fixme */
+ if (inet_pton(PF_INET, args->net.ipaddr, &ina)) {
+ fd = ipv4_connect(&ina, args->net.port, 3);
+ } else if (inet_pton(PF_INET6, args->net.ipaddr, &in6a)) {
+ fd = ipv6_connect(&in6a, args->net.port, 3);
+ }
+
+ if (fd < 0) {
+ printf("Unable to connect to fence_virtd host %s:%d %s\n",
+ args->net.ipaddr, args->net.port, strerror(errno));
+ return 1;
+ }
+
+ ret = _write_retry(fd, &freq, sizeof(freq), NULL);
+ if (ret != sizeof(freq)) {
+ perror("write");
+ close(fd);
+ return 1;
+ }
+
+ switch (args->net.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ ret = tcp_exchange(fd, args->net.auth, key, key_len,
+ args->timeout);
+ break;
+ /* case AUTH_X509:
+ return ssl_exchange(...); */
+ default:
+ dbg_printf(3, "Unknown auth type: %d\n", args->net.auth);
+ ret = 1;
+ break;
+ }
+
+ close(fd);
+ return ret;
+}
diff --git a/agents/virt/client/vsock.c b/agents/virt/client/vsock.c
new file mode 100644
index 0000000..7557c54
--- /dev/null
+++ b/agents/virt/client/vsock.c
@@ -0,0 +1,176 @@
+/*
+ Copyright Red Hat, Inc. 2017
+
+ 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, 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; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <nss.h>
+#include <sys/socket.h>
+#include <linux/vm_sockets.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "debug.h"
+#include "fdops.h"
+#include "client.h"
+
+static int
+sock_exchange(int fd, fence_auth_type_t auth, void *key,
+ size_t key_len, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ char ret = 1;
+
+ /* Ok, we're connected */
+ dbg_printf(3, "Issuing challenge\n");
+ if (sock_challenge(fd, auth, key, key_len, timeout) <= 0) {
+ /* Challenge failed */
+ printf("Invalid response to challenge\n");
+ return 1;
+ }
+
+ /* Now they'll send us one, so we need to respond here */
+ dbg_printf(3, "Responding to challenge\n");
+ if (sock_response(fd, auth, key, key_len, timeout) <= 0) {
+ printf("Invalid response to challenge\n");
+ return 1;
+ }
+
+ dbg_printf(2, "vsock Exchange + Authentication done... \n");
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ ret = 1;
+ dbg_printf(3, "Waiting for return value from fence_virtd host\n");
+ if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0)
+ return -1;
+
+ /* Read return code */
+ if (_read_retry(fd, &ret, 1, &tv) < 0)
+ ret = 1;
+
+ if (ret == (char)RESP_HOSTLIST) /* hostlist */ {
+ do_read_hostlist(fd, timeout);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+int
+vsock_fence_virt(fence_virt_args_t *args)
+{
+ char key[MAX_KEY_LEN];
+ struct timeval tv;
+ int key_len = 0, fd = -1;
+ int ret;
+ struct sockaddr_vm svm;
+ fence_req_t freq;
+
+ /* Initialize NSS; required to do hashing, as silly as that
+ sounds... */
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ printf("Could not initialize NSS\n");
+ return 1;
+ }
+
+ if (args->net.auth != AUTH_NONE || args->net.hash != HASH_NONE) {
+ key_len = read_key_file(args->net.key_file, key, sizeof(key));
+ if (key_len < 0) {
+ printf("Could not read %s; trying without "
+ "authentication\n", args->net.key_file);
+ args->net.auth = AUTH_NONE;
+ args->net.hash = HASH_NONE;
+ key_len = 0;
+ }
+ }
+
+ /* Same wire protocol as fence_xvm */
+ memset(&freq, 0, sizeof(freq));
+ if (args->domain && strlen((char *)args->domain))
+ strncpy((char *)freq.domain, args->domain, sizeof(freq.domain) - 1);
+ freq.request = args->op;
+ freq.hashtype = args->net.hash;
+ freq.flags = 0;
+ if (args->flags & F_USE_UUID)
+ freq.flags |= RF_UUID;
+ gettimeofday(&tv, NULL);
+ freq.seqno = (uint32_t) tv.tv_usec;
+ sign_request(&freq, key, key_len);
+
+ fd = socket(PF_VSOCK, SOCK_STREAM, 0);
+ if (fd < 0) {
+ printf("Unable to create vsock: %s", strerror(errno));
+ return 1;
+ }
+
+ memset(&svm, 0, sizeof(svm));
+ svm.svm_family = AF_VSOCK;
+ svm.svm_cid = args->net.cid;
+ svm.svm_port = args->net.port;
+
+ if (connect(fd, (struct sockaddr *) &svm, sizeof(svm)) < 0) {
+ printf("Unable to connect to fence_virtd host %d:%d %s\n",
+ args->net.cid, args->net.port, strerror(errno));
+ close(fd);
+ return 1;
+ }
+
+ ret = _write_retry(fd, &freq, sizeof(freq), NULL);
+ if (ret != sizeof(freq)) {
+ perror("write");
+ close(fd);
+ return 1;
+ }
+
+ switch (args->net.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ ret = sock_exchange(fd, args->net.auth, key, key_len,
+ args->timeout);
+ break;
+ /* case AUTH_X509:
+ return ssl_exchange(...); */
+ default:
+ dbg_printf(3, "Unknown auth type: %d\n", args->net.auth);
+ ret = 1;
+ break;
+ }
+
+ close(fd);
+ return ret;
+}