summaryrefslogtreecommitdiffstats
path: root/agents/virt
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
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 '')
-rw-r--r--agents/virt/Makefile.am31
-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
-rw-r--r--agents/virt/common/Makefile.am24
-rw-r--r--agents/virt/common/bcast.c347
-rw-r--r--agents/virt/common/debug.c38
-rw-r--r--agents/virt/common/fdops.c202
-rw-r--r--agents/virt/common/ip_lookup.c326
-rw-r--r--agents/virt/common/log.c204
-rw-r--r--agents/virt/common/mcast.c388
-rw-r--r--agents/virt/common/simple_auth.c466
-rw-r--r--agents/virt/common/tcp.c386
-rw-r--r--agents/virt/config/Makefile.am44
-rw-r--r--agents/virt/config/config-stack.h38
-rw-r--r--agents/virt/config/config.l106
-rw-r--r--agents/virt/config/config.y140
-rw-r--r--agents/virt/config/fence_virt.conf20
-rw-r--r--agents/virt/config/simpleconfig.c494
-rw-r--r--agents/virt/docs/README125
-rw-r--r--agents/virt/docs/TODO7
-rw-r--r--agents/virt/docs/architecture.txt16
-rw-r--r--agents/virt/docs/fence_virt.txt127
-rw-r--r--agents/virt/fence_virtd.service.in23
-rw-r--r--agents/virt/include/bcast.h16
-rw-r--r--agents/virt/include/client.h9
-rw-r--r--agents/virt/include/debug.h31
-rw-r--r--agents/virt/include/fdops.h14
-rw-r--r--agents/virt/include/history.h25
-rw-r--r--agents/virt/include/ip_lookup.h40
-rw-r--r--agents/virt/include/list.h84
-rw-r--r--agents/virt/include/mcast.h32
-rw-r--r--agents/virt/include/options.h99
-rw-r--r--agents/virt/include/server_plugin.h130
-rw-r--r--agents/virt/include/simple_auth.h35
-rw-r--r--agents/virt/include/simpleconfig.h56
-rw-r--r--agents/virt/include/static_map.h34
-rw-r--r--agents/virt/include/tcp.h27
-rw-r--r--agents/virt/include/tcp_listener.h7
-rw-r--r--agents/virt/include/xvm.h158
-rw-r--r--agents/virt/man/.gitignore2
-rw-r--r--agents/virt/man/Makefile.am28
-rw-r--r--agents/virt/man/fence_virt.conf.5335
-rw-r--r--agents/virt/man/fence_virtd.853
-rw-r--r--agents/virt/man/fence_xvm.81
-rw-r--r--agents/virt/server/Makefile.am79
-rw-r--r--agents/virt/server/config.c698
-rw-r--r--agents/virt/server/cpg-virt.c643
-rw-r--r--agents/virt/server/cpg.c411
-rw-r--r--agents/virt/server/cpg.h29
-rw-r--r--agents/virt/server/daemon_init.c215
-rw-r--r--agents/virt/server/history.c124
-rw-r--r--agents/virt/server/libvirt.c359
-rw-r--r--agents/virt/server/main.c281
-rw-r--r--agents/virt/server/mcast.c622
-rw-r--r--agents/virt/server/plugin.c417
-rw-r--r--agents/virt/server/serial.c459
-rw-r--r--agents/virt/server/serial.h20
-rw-r--r--agents/virt/server/static_map.c237
-rw-r--r--agents/virt/server/tcp.c575
-rw-r--r--agents/virt/server/uuid-test.c66
-rw-r--r--agents/virt/server/uuid-test.h14
-rw-r--r--agents/virt/server/virt-serial.c444
-rw-r--r--agents/virt/server/virt-sockets.c242
-rw-r--r--agents/virt/server/virt.c630
-rw-r--r--agents/virt/server/virt.h62
-rw-r--r--agents/virt/server/vsock.c565
71 files changed, 14249 insertions, 0 deletions
diff --git a/agents/virt/Makefile.am b/agents/virt/Makefile.am
new file mode 100644
index 0000000..4f50c60
--- /dev/null
+++ b/agents/virt/Makefile.am
@@ -0,0 +1,31 @@
+###############################################################################
+###############################################################################
+##
+## Copyright (C) 2009-2021 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
+
+EXTRA_DIST = docs/architecture.txt docs/fence_virt.txt \
+ docs/README docs/TODO include \
+ fence_virtd.service.in
+
+SUBDIRS = config common client server man
+
+all-local: fence_virtd.service
+
+clean-local:
+ rm -f $(SPEC) fence_virtd.service
+
+fence_virtd.service: fence_virtd.service.in
+ SBINDIR="@sbindir@"; \
+ INITCONFDIR="@initconfdir@"; \
+ cat $^ > $@ ; \
+ echo "EnvironmentFile=-$$INITCONFDIR/fence_virtd" >> $@ ;\
+ echo "ExecStart=$$SBINDIR/fence_virtd \$$FENCE_VIRTD_ARGS" >> $@
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;
+}
diff --git a/agents/virt/common/Makefile.am b/agents/virt/common/Makefile.am
new file mode 100644
index 0000000..d5e4314
--- /dev/null
+++ b/agents/virt/common/Makefile.am
@@ -0,0 +1,24 @@
+###############################################################################
+###############################################################################
+##
+## 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
+
+noinst_LTLIBRARIES = libfence_virt.la
+
+libfence_virt_la_SOURCES = mcast.c ip_lookup.c simple_auth.c tcp.c \
+ debug.c fdops.c log.c
+
+libfence_virt_la_CFLAGS = $(VIRT_AM_CFLAGS) $(nss_CFLAGS) $(AM_CFLAGS) -Wno-cast-align
+
+libfence_virt_la_LDFLAGS = $(VIRT_AM_LDFLAGS) $(VIRT_COMMON_LDFLAGS)
+
+libfence_virt_la_LIBADD = $(nss_LIBS)
diff --git a/agents/virt/common/bcast.c b/agents/virt/common/bcast.c
new file mode 100644
index 0000000..2b498b7
--- /dev/null
+++ b/agents/virt/common/bcast.c
@@ -0,0 +1,347 @@
+/*
+ * 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/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>
+
+/* Local includes */
+#include "bcast.h"
+#include "debug.h"
+
+LOGSYS_DECLARE_SUBSYS ("XVM", SYSLOGLEVEL);
+
+/**
+ Sets up a multicast receive socket
+ */
+int
+ipv4_bcast_recv_sk(char *addr, int port)
+{
+ int sock, val;
+ struct sockaddr_in sin;
+
+ /* Store broadcast address */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = PF_INET;
+ sin.sin_port = htons(port);
+ if (inet_pton(PF_INET, addr,
+ (void *)&sin.sin_addr.s_addr) < 0) {
+ log_printf(LOG_ERR, "Invalid broadcast address: %s\n", addr);
+ return -1;
+ }
+
+ dbg_printf(4, "Setting up ipv4 broadcast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ log_printf(LOG_ERR, "socket: %s\n", strerror(errno));
+ close(sock);
+ sock = -1;
+ return 1;
+ }
+
+ val = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, 1, sizeof(val)) < 0) {
+ log_printf(LOG_ERR, "setsockopt: %s\n", strerror(errno));
+ close(sock);
+ return 1;
+ }
+
+ /*
+ * Bind broadcast address
+ */
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up multicast send socket
+ */
+int
+ipv4_bcast_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len, int ttl)
+{
+ int val;
+ struct ip_mreq mreq;
+ struct sockaddr_in mcast;
+ struct sockaddr_in src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Store multicast address */
+ mcast.sin_family = PF_INET;
+ mcast.sin_port = htons(port);
+ if (inet_pton(PF_INET, addr,
+ (void *)&mcast.sin_addr.s_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+ mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr;
+
+ /* Store sending address */
+ src.sin_family = PF_INET;
+ src.sin_port = htons(port);
+ if (inet_pton(PF_INET, send_addr,
+ (void *)&src.sin_addr.s_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+ mreq.imr_interface.s_addr = src.sin_addr.s_addr;
+
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 1)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 2)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr,
+ sizeof(src.sin_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * set time to live to 2 hops.
+ */
+ dbg_printf(4, "Setting TTL to %d for fd%d\n", ttl, sock);
+ val = ttl;
+ if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+
+/**
+ Sets up a multicast receive (ipv6) socket
+ */
+int
+ipv6_recv_sk(char *addr, int port)
+{
+ int sock, val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 sin;
+
+ memset(&mreq, 0, sizeof(mreq));
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&sin.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr,
+ sizeof(struct in6_addr));
+
+
+ /********************************
+ * SET UP MULTICAST RECV SOCKET *
+ ********************************/
+ dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ printf("socket: %s\n", strerror(errno));
+ close(sock);
+ sock = -1;
+ return 1;
+ }
+
+ /*
+ * When using Multicast, bind to the LOCAL address, not the MULTICAST
+ * address.
+ */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ sin.sin6_addr = in6addr_any;
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in6)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join multicast group
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast to socket %s: %s\n",
+ addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up ipv6 multicast send socket
+ */
+int
+ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len, int ttl)
+{
+ int val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 mcast;
+ struct sockaddr_in6 src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in6)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+
+ /* Store multicast address */
+ mcast.sin6_family = PF_INET6;
+ mcast.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&mcast.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr,
+ sizeof(struct in6_addr));
+
+ /* Store sending address */
+ src.sin6_family = PF_INET6;
+ src.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, send_addr,
+ (void *)&src.sin6_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group (part 2)
+ */
+ /*
+ if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr,
+ sizeof(src.sin6_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+ */
+
+ /*
+ * set time to live to 2 hops.
+ */
+ val = ttl;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
diff --git a/agents/virt/common/debug.c b/agents/virt/common/debug.c
new file mode 100644
index 0000000..5cf5359
--- /dev/null
+++ b/agents/virt/common/debug.c
@@ -0,0 +1,38 @@
+/*
+ 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 "debug.h"
+
+static int _debug = 0;
+
+void
+dset(int threshold)
+{
+ _debug = threshold;
+ dbg_printf(3, "Debugging threshold is now %d\n", threshold);
+}
+
+int
+dget(void)
+{
+ return _debug;
+}
diff --git a/agents/virt/common/fdops.c b/agents/virt/common/fdops.c
new file mode 100644
index 0000000..329e9b7
--- /dev/null
+++ b/agents/virt/common/fdops.c
@@ -0,0 +1,202 @@
+/*
+ Copyright Red Hat, Inc. 2002-2003
+
+ 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.
+*/
+/** @file
+ * Wrapper functions around read/write/select to retry in the event
+ * of interrupts.
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "fdops.h"
+
+/**
+ * This is a wrapper around select which will retry in the case we receive
+ * EINTR. This is necessary for _read_retry, since it wouldn't make sense
+ * to have _read_retry terminate if and only if two EINTRs were received
+ * in a row - one during the read() call, one during the select call...
+ *
+ * See select(2) for description of parameters.
+ */
+int
+_select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds,
+ struct timeval *timeout)
+{
+ int rv;
+
+ while (1) {
+ rv = select(fdmax, rfds, wfds, xfds, timeout);
+ if (rv == -1) {
+ /* return on EBADF/EINVAL/ENOMEM; continue on EINTR/EAGAIN/ENOMEM */
+ if (errno == EINTR || errno == EAGAIN || errno == ENOMEM)
+ continue;
+ }
+ return rv;
+ }
+}
+
+/**
+ * Retries a write in the event of a non-blocked interrupt signal.
+ *
+ * @param fd File descriptor to which we are writing.
+ * @param buf Data buffer to send.
+ * @param count Number of bytes in buf to send.
+ * @param timeout (struct timeval) telling us how long we should retry.
+ * @return The number of bytes written to the file descriptor,
+ * or -1 on error (with errno set appropriately).
+ */
+ssize_t
+_write_retry(int fd, void *buf, int count, struct timeval * timeout)
+{
+ int n, total = 0, remain = count, rv = 0;
+ fd_set wfds, xfds;
+ char *tmp_buf = (char *)buf;
+
+ while (total < count) {
+
+ /* Create the write FD set of 1... */
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+ FD_ZERO(&xfds);
+ FD_SET(fd, &xfds);
+
+ /* wait for the fd to be available for writing */
+ rv = _select_retry(fd + 1, NULL, &wfds, &xfds, timeout);
+ if (rv == -1)
+ return -1;
+ else if (rv == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (FD_ISSET(fd, &xfds)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ /*
+ * Attempt to write to fd
+ */
+ n = write(fd, tmp_buf + total, remain);
+
+ /*
+ * When we know our fd was select()ed and we receive 0 bytes
+ * when we write, the fd was closed.
+ */
+ if ((n == 0) && (rv == 1)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ if (n == -1) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ /*
+ * Not ready?
+ */
+ continue;
+ }
+
+ /* Other errors: EIO, EINVAL, etc */
+ return -1;
+ }
+
+ total += n;
+ remain -= n;
+ }
+
+ return total;
+}
+
+/**
+ * Retry reads until we (a) time out or (b) get our data. Of course, if
+ * timeout is NULL, it'll wait forever.
+ *
+ * @param sockfd File descriptor we want to read from.
+ * @param buf Preallocated buffer into which we will read data.
+ * @param count Number of bytes to read.
+ * @param timeout (struct timeval) describing how long we should retry.
+ * @return The number of bytes read on success, or -1 on failure.
+ Note that we will always return (count) or (-1).
+ */
+ssize_t
+_read_retry(int sockfd, void *buf, int count, struct timeval * timeout)
+{
+ int n, total = 0, remain = count, rv = 0;
+ fd_set rfds, xfds;
+ char *tmp_buf = (char *)buf;
+
+ while (total < count) {
+ FD_ZERO(&rfds);
+ FD_SET(sockfd, &rfds);
+ FD_ZERO(&xfds);
+ FD_SET(sockfd, &xfds);
+
+ /*
+ * Select on the socket, in case it closes while we're not
+ * looking...
+ */
+ rv = _select_retry(sockfd + 1, &rfds, NULL, &xfds, timeout);
+ if (rv == -1)
+ return -1;
+ else if (rv == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (FD_ISSET(sockfd, &xfds)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ /*
+ * Attempt to read off the socket
+ */
+ n = read(sockfd, tmp_buf + total, remain);
+
+ /*
+ * When we know our socket was select()ed and we receive 0 bytes
+ * when we read, the socket was closed.
+ */
+ if ((n == 0) && (rv == 1)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ if (n == -1) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ /*
+ * Not ready? Wait for data to become available
+ */
+ continue;
+ }
+
+ /* Other errors: EPIPE, EINVAL, etc */
+ return -1;
+ }
+
+ total += n;
+ remain -= n;
+ }
+
+ return total;
+}
diff --git a/agents/virt/common/ip_lookup.c b/agents/virt/common/ip_lookup.c
new file mode 100644
index 0000000..aded8ea
--- /dev/null
+++ b/agents/virt/common/ip_lookup.c
@@ -0,0 +1,326 @@
+/*
+ Copyright Red Hat, Inc. 2004, 2006
+
+ The Magma Cluster API Library is free software; you can redistribute
+ it and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either version
+ 2.1 of the License, or (at your option) any later version.
+
+ The Magma Cluster API Library is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+ */
+/** @file
+ * Build lists of IPs on the system, excepting loopback ipv6 link-local
+ */
+
+#include "config.h"
+
+#include <asm/types.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+
+/* Local includes */
+#include "ip_lookup.h"
+#include "debug.h"
+
+static int
+send_addr_dump(int fd, int family)
+{
+ struct nlmsghdr *nh;
+ struct rtgenmsg *g;
+ char buf[256];
+ struct sockaddr_nl addr;
+
+ memset(&addr,0,sizeof(addr));
+ addr.nl_family = PF_NETLINK;
+
+ memset(buf, 0, sizeof(buf));
+ nh = (struct nlmsghdr *)buf;
+ g = (struct rtgenmsg *)(buf + sizeof(struct nlmsghdr));
+
+ nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+ nh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+ nh->nlmsg_type = RTM_GETADDR;
+ g->rtgen_family = family;
+
+ return sendto(fd, buf, nh->nlmsg_len, 0, (struct sockaddr *)&addr,
+ sizeof(addr));
+}
+
+
+static int
+add_ip(ip_list_t *ipl, char *ipaddr, char family)
+{
+ ip_addr_t *ipa;
+
+ if (family == PF_INET6) {
+ /* Avoid loopback */
+ if (!strcmp(ipaddr, "::1"))
+ return -1;
+
+ /* Avoid link-local addresses */
+ if (!strncmp(ipaddr, "fe80", 4))
+ return -1;
+ if (!strncmp(ipaddr, "fe90", 4))
+ return -1;
+ if (!strncmp(ipaddr, "fea0", 4))
+ return -1;
+ if (!strncmp(ipaddr, "feb0", 4))
+ return -1;
+ }
+
+ ipa = calloc(1, sizeof(*ipa));
+ if (!ipa)
+ return -1;
+ ipa->ipa_family = family;
+ ipa->ipa_address = strdup(ipaddr);
+
+ dbg_printf(4, "Adding IP %s to list (family %d)\n", ipaddr, family);
+ TAILQ_INSERT_TAIL(ipl, ipa, ipa_entries);
+
+ return 0;
+}
+
+
+static int
+add_ip_addresses(int family, ip_list_t *ipl)
+{
+ /* List ipv4 addresses */
+ struct nlmsghdr *nh;
+ struct ifaddrmsg *ifa;
+ struct rtattr *rta, *nrta;
+ struct nlmsgerr *err;
+ char buf[10240];
+ char outbuf[256];
+ char label[256];
+ int x, fd, len;
+
+ dbg_printf(5, "Connecting to Netlink...\n");
+ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ perror("socket");
+ exit(1);
+ }
+
+ dbg_printf(5, "Sending address dump request\n");
+ if (send_addr_dump(fd, family) < 0) {
+ perror("sendto");
+ close(fd);
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+
+ dbg_printf(5, "Waiting for response\n");
+ x = recvfrom(fd, buf, sizeof(buf), 0, NULL, 0);
+ if (x < 0) {
+ perror("recvfrom");
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(5, "Received %d bytes\n", x);
+
+ nh = (struct nlmsghdr *)buf;
+ while (NLMSG_OK(nh, x)) {
+
+ switch(nh->nlmsg_type) {
+ case NLMSG_DONE:
+ close(fd);
+ return 0;
+
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr*)NLMSG_DATA(nh);
+ if (nh->nlmsg_len <
+ NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr, "ERROR truncated");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ close(fd);
+ return -1;
+
+ case RTM_NEWADDR:
+ break;
+
+ default:
+ nh = NLMSG_NEXT(nh, x);
+ continue;
+ }
+
+ /* RTM_NEWADDR */
+ len = NLMSG_PAYLOAD(nh,0);
+ ifa = NLMSG_DATA(nh);
+
+ /* Make sure we got the type we expect back */
+ if (ifa->ifa_family != family) {
+ nh = NLMSG_NEXT(nh, x);
+ continue;
+ }
+
+ rta = IFA_RTA(ifa);
+ len -= sizeof(struct ifaddrmsg);
+ do {
+ /* Make sure we've got a valid rtaddr field */
+ if (!RTA_OK(rta, len)) {
+ dbg_printf(5, "!RTA_OK(rta, len)\n");
+ break;
+ }
+
+ if (rta->rta_type == IFA_ADDRESS) {
+ inet_ntop(family, RTA_DATA(rta), outbuf,
+ sizeof(outbuf) );
+ add_ip(ipl, outbuf, family);
+ }
+
+ if (rta->rta_type == IFA_LABEL) {
+ memset(label, 0, sizeof(label));
+ strncpy(label, (char *)RTA_DATA(rta), sizeof(label) - 1);
+ dbg_printf(5, "Skipping label: %s\n",
+ label);
+ }
+
+ nrta = RTA_NEXT(rta, len);
+ len -= (nrta - rta);
+ rta = nrta;
+ } while (RTA_OK(rta, len));
+
+ nh = NLMSG_NEXT(nh, x);
+ }
+
+ dbg_printf(5, "Closing Netlink connection\n");
+ close(fd);
+ return 0;
+}
+
+
+int
+ip_search(ip_list_t *ipl, char *ip_name)
+{
+ ip_addr_t *ipa;
+
+ dbg_printf(5, "Looking for IP address %s in IP list %p...", ip_name, ipl);
+ ipa = ipl->tqh_first;
+ for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) {
+ if (!strcmp(ip_name, ipa->ipa_address)) {
+ dbg_printf(4,"Found\n");
+ return 0;
+ }
+ }
+ dbg_printf(5, "Not found\n");
+ return 1;
+}
+
+
+int
+ip_free_list(ip_list_t *ipl)
+{
+ ip_addr_t *ipa;
+
+ dbg_printf(5, "Tearing down IP list @ %p\n", ipl);
+ while ((ipa = ipl->tqh_first)) {
+ TAILQ_REMOVE(ipl, ipa, ipa_entries);
+ free(ipa->ipa_address);
+ free(ipa);
+ }
+ return 0;
+}
+
+
+int
+ip_build_list(ip_list_t *ipl)
+{
+ dbg_printf(5, "Build IP address list\n");
+ TAILQ_INIT(ipl);
+ if (add_ip_addresses(PF_INET6, ipl) < 0) {
+ ip_free_list(ipl);
+ return -1;
+ }
+ if (add_ip_addresses(PF_INET, ipl) < 0) {
+ ip_free_list(ipl);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ Look up the interface name which corresponds to the given hostname and
+ return the list of matching attrinfo structures. We do this by looking
+ up all the possible physical and virtual network interfaces on the machine
+ and checking the hostname/IP mappings for each active IP address incurred.
+
+ @param nodename Interface name
+ @param ret_ai Structure pointer to allocate & return.
+ @return -1 on failure or 0 on success.
+ */
+int
+ip_lookup(char *nodename, struct addrinfo **ret_ai)
+{
+ char ip_name[256];
+ struct addrinfo *ai = NULL;
+ struct addrinfo *n;
+ void *p;
+ ip_list_t ipl;
+ int ret = -1;
+
+ dbg_printf(5, "Looking for IP matching %s\n", nodename);
+ /* Build list of IP addresses configured locally */
+ if (ip_build_list(&ipl) < 0)
+ return -1;
+
+ /* Get list of addresses for the host-name/ip */
+ if (getaddrinfo(nodename, NULL, NULL, &ai) != 0)
+ return -1;
+
+
+ /* Traverse list of addresses for given host-name/ip */
+ for (n = ai; n; n = n->ai_next) {
+ if (n->ai_family != PF_INET && n->ai_family != PF_INET6)
+ continue;
+
+ if (n->ai_family == PF_INET)
+ p = &(((struct sockaddr_in *)n->ai_addr)->sin_addr);
+ else
+ p = &(((struct sockaddr_in6 *)n->ai_addr)->sin6_addr);
+
+ if (!inet_ntop(n->ai_family, p, ip_name,
+ sizeof(ip_name)))
+ continue;
+
+ /* Search local interfaces for this IP address */
+ if (ip_search(&ipl, ip_name) != 0)
+ continue;
+
+ /* Found it */
+ ret = 0;
+ break;
+ }
+
+ /* Clean up */
+ if (!ret_ai)
+ freeaddrinfo(ai);
+ else
+ *ret_ai = ai;
+
+ ip_free_list(&ipl);
+
+ return ret;
+}
+
diff --git a/agents/virt/common/log.c b/agents/virt/common/log.c
new file mode 100644
index 0000000..61018ed
--- /dev/null
+++ b/agents/virt/common/log.c
@@ -0,0 +1,204 @@
+/**
+ * Syslog wrapper that does not block
+ *
+ * Lon Hohberger, 2009
+ */
+
+#include "config.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "list.h"
+
+struct log_entry {
+ list_head();
+ char *message;
+ int sev;
+ int bufsz;
+};
+
+#define MAX_QUEUE_LENGTH 10
+#define LOGLEN 256
+
+static struct log_entry *_log_entries = NULL;
+static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t log_cond = PTHREAD_COND_INITIALIZER;
+static int log_size = 0;
+static int dropped = 0;
+static pthread_t thread_id = 0;
+
+void __real_syslog(int severity, const char *fmt, ...);
+void __wrap_syslog(int severity, const char *fmt, ...);
+void __wrap_closelog(void);
+
+static void *
+_log_thread(void *arg)
+{
+ struct timeval tv;
+ struct timespec ts;
+ struct log_entry *entry;
+
+ do {
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec + 10;
+ ts.tv_nsec = tv.tv_usec;
+
+ pthread_mutex_lock(&log_mutex);
+
+ while (!(entry = _log_entries)) {
+ if (pthread_cond_timedwait(&log_cond,
+ &log_mutex,
+ &ts) == ETIMEDOUT)
+ goto out;
+ }
+
+ list_remove(&_log_entries, entry);
+ --log_size;
+ if (log_size < 0)
+ raise(SIGSEGV);
+ pthread_mutex_unlock(&log_mutex);
+
+ __real_syslog(entry->sev, entry->message);
+ free(entry->message);
+ free(entry);
+ } while (1);
+
+out:
+ thread_id = (pthread_t)0;
+ pthread_mutex_unlock(&log_mutex);
+ return NULL;
+}
+
+
+static int
+insert_entry(int sev, char *buf, int bufsz)
+{
+ struct log_entry *lent;
+ pthread_attr_t attrs;
+
+ lent = malloc(sizeof(*lent));
+ if (!lent)
+ return -1;
+ lent->sev = sev;
+ lent->message = buf;
+ lent->bufsz = bufsz;
+
+ pthread_mutex_lock(&log_mutex);
+ if (log_size >= MAX_QUEUE_LENGTH) {
+ free(lent->message);
+ free(lent);
+
+ ++dropped;
+ lent = (struct log_entry *)(le(_log_entries)->le_prev);
+
+ lent->sev = LOG_WARNING;
+ snprintf(lent->message, lent->bufsz,
+ "%d message(s) lost due to syslog load\n",
+ dropped + 1);
+ /* Dropped +1 because we overwrote a message to
+ * give the 'dropped' message */
+ } else {
+ ++log_size;
+ dropped = 0;
+ list_insert(&_log_entries, lent);
+ }
+
+ if (!thread_id) {
+ pthread_attr_init(&attrs);
+ pthread_attr_setinheritsched(&attrs, PTHREAD_INHERIT_SCHED);
+
+ if (pthread_create(&thread_id, &attrs, _log_thread, NULL) < 0)
+ thread_id = 0;
+ pthread_mutex_unlock(&log_mutex);
+ } else {
+ pthread_mutex_unlock(&log_mutex);
+ pthread_cond_signal(&log_cond);
+ }
+
+ return 0;
+}
+
+
+__attribute__((__format__ (__printf__, 2, 0)))
+void
+__wrap_syslog(int severity, const char *fmt, ...)
+{
+ va_list args;
+ char *logmsg;
+
+ logmsg = malloc(LOGLEN);
+ if (!logmsg)
+ return;
+ memset(logmsg, 0, LOGLEN);
+
+ va_start(args, fmt);
+ vsnprintf(logmsg + strlen(logmsg), LOGLEN - strlen(logmsg),
+ fmt, args);
+ va_end(args);
+
+ insert_entry(severity, logmsg, LOGLEN);
+
+ return;
+}
+
+
+void __real_closelog(void);
+
+void
+__wrap_closelog(void)
+{
+ struct log_entry *lent;
+#ifdef DEBUG
+ int lost = 0;
+#endif
+
+ if (thread_id != 0) {
+ pthread_cancel(thread_id);
+ pthread_join(thread_id, NULL);
+ thread_id = 0;
+ }
+ __real_closelog();
+ while (_log_entries) {
+#ifdef DEBUG
+ ++lost;
+#endif
+ lent = _log_entries;
+ list_remove(&_log_entries, lent);
+ free(lent->message);
+ free(lent);
+ }
+
+#ifdef DEBUG
+ printf("%d lost\n", lost);
+#endif
+}
+
+
+#ifdef STANDALONE
+int
+main(int argc, char**argv)
+{
+ int x;
+
+ for (x = 0; x < 100; x++) {
+ syslog(1, "Yo %d\n", x);
+ }
+ sleep(1);
+
+ closelog();
+
+ return 0;
+}
+
+#endif
diff --git a/agents/virt/common/mcast.c b/agents/virt/common/mcast.c
new file mode 100644
index 0000000..cc79c64
--- /dev/null
+++ b/agents/virt/common/mcast.c
@@ -0,0 +1,388 @@
+/*
+ Copyright Red Hat, Inc. 2003, 2004, 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/un.h>
+#include <sys/socket.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>
+
+/* Local includes */
+#include "mcast.h"
+#include "debug.h"
+
+/**
+ Sets up a multicast receive socket
+ */
+int
+ipv4_recv_sk(char *addr, int port, unsigned int ifindex)
+{
+ int sock;
+ struct ip_mreqn mreq;
+ struct sockaddr_in sin;
+
+ memset(&mreq, 0, sizeof(mreq));
+ memset(&sin, 0, sizeof(sin));
+
+ /* Store multicast address */
+ if (inet_pton(PF_INET, addr,
+ (void *)&mreq.imr_multiaddr.s_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ /********************************
+ * SET UP MULTICAST RECV SOCKET *
+ ********************************/
+ dbg_printf(4, "Setting up ipv4 multicast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ printf("socket: %s\n", strerror(errno));
+ sock = -1;
+ return 1;
+ }
+
+ /*
+ * When using Multicast, bind to the LOCAL address, not the MULTICAST
+ * address.
+ */
+ sin.sin_family = PF_INET;
+ sin.sin_port = htons(port);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join multicast group
+ */
+ /* mreq.imr_multiaddr.s_addr is set above */
+ if (ifindex == 0) {
+ dbg_printf(4, "Setting mcast addr to INADDR_ANY due to ifindex of 0\n");
+ mreq.imr_address.s_addr = htonl(INADDR_ANY);
+ } else {
+ mreq.imr_ifindex = ifindex;
+ }
+ dbg_printf(4, "Joining multicast group\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) == -1) {
+ printf("Failed to bind multicast receive socket to "
+ "%s: %s\n", addr, strerror(errno));
+ printf("Check network configuration.\n");
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up multicast send socket
+ */
+int
+ipv4_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len)
+{
+ int val;
+ struct ip_mreq mreq;
+ struct sockaddr_in mcast;
+ struct sockaddr_in src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&mcast, 0, sizeof(mcast));
+ memset(&src, 0, sizeof(src));
+
+ /* Store multicast address */
+ mcast.sin_family = PF_INET;
+ mcast.sin_port = htons(port);
+ if (inet_pton(PF_INET, addr,
+ (void *)&mcast.sin_addr.s_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+ mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr;
+
+ /* Store sending address */
+ src.sin_family = PF_INET;
+ src.sin_port = htons(port);
+ if (inet_pton(PF_INET, send_addr,
+ (void *)&src.sin_addr.s_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+ mreq.imr_interface.s_addr = src.sin_addr.s_addr;
+
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 1)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 2)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr,
+ sizeof(src.sin_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * set time to live to 2 hops.
+ */
+ dbg_printf(4, "Setting TTL to 2 for fd%d\n", sock);
+ val = 2;
+ if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+
+/**
+ Sets up a multicast receive (ipv6) socket
+ */
+int
+ipv6_recv_sk(char *addr, int port, unsigned int ifindex)
+{
+ int sock, val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 sin;
+
+ memset(&mreq, 0, sizeof(mreq));
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&sin.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr,
+ sizeof(struct in6_addr));
+
+ mreq.ipv6mr_interface = ifindex;
+
+ /********************************
+ * SET UP MULTICAST RECV SOCKET *
+ ********************************/
+ dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ printf("socket: %s\n", strerror(errno));
+ sock = -1;
+ return 1;
+ }
+
+ /*
+ * When using Multicast, bind to the LOCAL address, not the MULTICAST
+ * address.
+ */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ sin.sin6_addr = in6addr_any;
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in6)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join multicast group
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast to socket %s: %s\n",
+ addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up ipv6 multicast send socket
+ */
+int
+ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len)
+{
+ int val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 mcast;
+ struct sockaddr_in6 src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in6)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&mcast, 0, sizeof(mcast));
+ memset(&src, 0, sizeof(src));
+ memset(&mreq, 0, sizeof(mreq));
+
+ /* Store multicast address */
+ mcast.sin6_family = PF_INET6;
+ mcast.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&mcast.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr,
+ sizeof(struct in6_addr));
+
+ /* Store sending address */
+ src.sin6_family = PF_INET6;
+ src.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, send_addr,
+ (void *)&src.sin6_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group (part 2)
+ */
+ /*
+ if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr,
+ sizeof(src.sin6_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+ */
+
+ /*
+ * set time to live to 2 hops.
+ */
+ val = 2;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
diff --git a/agents/virt/common/simple_auth.c b/agents/virt/common/simple_auth.c
new file mode 100644
index 0000000..9f694c4
--- /dev/null
+++ b/agents/virt/common/simple_auth.c
@@ -0,0 +1,466 @@
+/*
+ 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 <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sechash.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "fdops.h"
+#include "simple_auth.h"
+#include "debug.h"
+
+
+static void
+print_hash(unsigned char *hash, size_t hashlen)
+{
+ int x;
+
+ for (x = 0; x < hashlen; x++)
+ printf("%02x", (hash[x]&0xff));
+}
+
+
+static int
+sha_sign(fence_req_t *req, void *key, size_t key_len)
+{
+ unsigned char hash[SHA512_LENGTH];
+ HASHContext *h;
+ HASH_HashType ht;
+ unsigned int rlen;
+ int devrand;
+ int ret;
+
+ switch(req->hashtype) {
+ case HASH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case HASH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case HASH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ dbg_printf(1, "Unknown hash type: %d\n", req->hashtype);
+ return -1;
+ }
+
+ dbg_printf(4, "Opening /dev/urandom\n");
+ devrand = open("/dev/urandom", O_RDONLY);
+ if (devrand < 0) {
+ dbg_printf(1, "Error: open: /dev/urandom: %s", strerror(errno));
+ return -1;
+ }
+
+ ret = _read_retry(devrand, req->random, sizeof(req->random), NULL);
+ if (ret <= 0) {
+ dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno));
+ close(devrand);
+ return -1;
+ }
+ close(devrand);
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht);
+ if (!h)
+ return -1;
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, (void *)req, sizeof(*req));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ memcpy(req->hash, hash, sizeof(req->hash));
+ return 0;
+}
+
+
+static int
+sha_verify(fence_req_t *req, void *key, size_t key_len)
+{
+ unsigned char hash[SHA512_LENGTH];
+ unsigned char pkt_hash[SHA512_LENGTH];
+ HASHContext *h = NULL;
+ HASH_HashType ht;
+ unsigned int rlen;
+ int ret;
+
+ switch(req->hashtype) {
+ case HASH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case HASH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case HASH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__);
+ return 0;
+ }
+
+ if (!key || !key_len) {
+ dbg_printf(3, "%s: Hashing requested when we have no key data\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht);
+ if (!h)
+ return 0;
+
+ memcpy(pkt_hash, req->hash, sizeof(pkt_hash));
+ memset(req->hash, 0, sizeof(req->hash));
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, (void *)req, sizeof(*req));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ memcpy(req->hash, pkt_hash, sizeof(req->hash));
+
+ ret = !memcmp(hash, pkt_hash, sizeof(hash));
+ if (!ret) {
+ printf("Hash mismatch:\nPKT = ");
+ print_hash(pkt_hash, sizeof(pkt_hash));
+ printf("\nEXP = ");
+ print_hash(hash, sizeof(hash));
+ printf("\n");
+ }
+
+ return ret;
+}
+
+
+int
+sign_request(fence_req_t *req, void *key, size_t key_len)
+{
+ memset(req->hash, 0, sizeof(req->hash));
+ switch(req->hashtype) {
+ case HASH_NONE:
+ dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__);
+ return 0;
+ case HASH_SHA1:
+ case HASH_SHA256:
+ case HASH_SHA512:
+ return sha_sign(req, key, key_len);
+ default:
+ break;
+ }
+ return -1;
+}
+
+
+int
+verify_request(fence_req_t *req, fence_hash_t min,
+ void *key, size_t key_len)
+{
+ if (req->hashtype < min) {
+ printf("Hash type not strong enough (%d < %d)\n",
+ req->hashtype, min);
+ return 0;
+ }
+ switch(req->hashtype) {
+ case HASH_NONE:
+ return 1;
+ case HASH_SHA1:
+ case HASH_SHA256:
+ case HASH_SHA512:
+ return sha_verify(req, key, key_len);
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int
+sha_challenge(int fd, fence_auth_type_t auth, void *key,
+ size_t key_len, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ unsigned char hash[MAX_HASH_LENGTH];
+ unsigned char challenge[MAX_HASH_LENGTH];
+ unsigned char response[MAX_HASH_LENGTH];
+ int devrand;
+ int ret;
+ HASHContext *h;
+ HASH_HashType ht;
+ unsigned int rlen;
+
+ devrand = open("/dev/urandom", O_RDONLY);
+ if (devrand < 0) {
+ dbg_printf(1, "Error: open /dev/urandom: %s", strerror(errno));
+ return 0;
+ }
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _read_retry(devrand, challenge, sizeof(challenge), &tv);
+ if (ret < 0) {
+ dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno));
+ close(devrand);
+ return 0;
+ }
+ close(devrand);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, challenge, sizeof(challenge), &tv);
+ if (ret < 0) {
+ dbg_printf(2, "Error: write: %s", strerror(errno));
+ return 0;
+ }
+
+ switch(auth) {
+ case HASH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case HASH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case HASH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ return 0;
+ }
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht);
+ if (!h)
+ return 0;
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, challenge, sizeof(challenge));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ memset(response, 0, sizeof(response));
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) {
+ dbg_printf(0, "Error: select: %s\n", strerror(errno));
+ return 0;
+ }
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _read_retry(fd, response, sizeof(response), &tv);
+ if (ret < 0) {
+ dbg_printf(0, "Error reading challenge response: %s", strerror(errno));
+ return 0;
+ } else if (ret < sizeof(response)) {
+ dbg_printf(0,
+ "read data from socket is too short(actual: %d, expected: %zu)\n",
+ ret, sizeof(response));
+ return 0;
+ }
+
+ ret = !memcmp(response, hash, sizeof(response));
+ if (!ret) {
+ printf("Hash mismatch:\nC = ");
+ print_hash(challenge, sizeof(challenge));
+ printf("\nH = ");
+ print_hash(hash, sizeof(hash));
+ printf("\nR = ");
+ print_hash(response, sizeof(response));
+ printf("\n");
+ }
+
+ return ret;
+}
+
+
+static int
+sha_response(int fd, fence_auth_type_t auth, void *key,
+ size_t key_len, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ unsigned char challenge[MAX_HASH_LENGTH];
+ unsigned char hash[MAX_HASH_LENGTH];
+ HASHContext *h;
+ HASH_HashType ht;
+ unsigned int rlen;
+ int ret;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) {
+ dbg_printf(2, "Error: select: %s\n", strerror(errno));
+ return 0;
+ }
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (_read_retry(fd, challenge, sizeof(challenge), &tv) < 0) {
+ dbg_printf(2, "Error reading challenge hash: %s\n", strerror(errno));
+ return 0;
+ }
+
+ switch(auth) {
+ case AUTH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case AUTH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case AUTH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__);
+ return 0;
+ }
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht); /* */
+ if (!h)
+ return 0;
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, challenge, sizeof(challenge));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, hash, sizeof(hash), &tv);
+ if (ret < 0) {
+ perror("write");
+ return 0;
+ } else if (ret < sizeof(hash)) {
+ dbg_printf(2,
+ "Only part of hash is written(actual: %d, expected: %zu)\n",
+ ret,
+ sizeof(hash));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+sock_challenge(int fd, fence_auth_type_t auth, void *key, size_t key_len,
+ int timeout)
+{
+ switch(auth) {
+ case AUTH_NONE:
+ dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__);
+ return 1;
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ return sha_challenge(fd, auth, key, key_len, timeout);
+ default:
+ break;
+ }
+ return -1;
+}
+
+
+int
+sock_response(int fd, fence_auth_type_t auth, void *key, size_t key_len,
+ int timeout)
+{
+ switch(auth) {
+ case AUTH_NONE:
+ dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__);
+ return 1;
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ return sha_response(fd, auth, key, key_len, timeout);
+ default:
+ break;
+ }
+ return -1;
+}
+
+
+int
+read_key_file(char *file, char *key, size_t max_len)
+{
+ int fd;
+ int nread, remain = max_len;
+ char *p;
+
+ dbg_printf(3, "Reading in key file %s into %p (%d max size)\n",
+ file, key, (int)max_len);
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ dbg_printf(2, "Error opening key file: %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(key, 0, max_len);
+ p = key;
+ remain = max_len;
+
+ while (remain) {
+ nread = read(fd, p, remain);
+ if (nread < 0) {
+ if (errno == EINTR)
+ continue;
+ dbg_printf(2, "Error from read: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (nread == 0) {
+ dbg_printf(3, "Stopped reading @ %d bytes\n",
+ (int)max_len-remain);
+ break;
+ }
+
+ p += nread;
+ remain -= nread;
+ }
+
+ close(fd);
+ dbg_printf(3, "Actual key length = %d bytes\n", (int)max_len-remain);
+
+ return (int)(max_len - remain);
+}
diff --git a/agents/virt/common/tcp.c b/agents/virt/common/tcp.c
new file mode 100644
index 0000000..5796770
--- /dev/null
+++ b/agents/virt/common/tcp.c
@@ -0,0 +1,386 @@
+/*
+ Copyright Red Hat, Inc. 2002-2004, 2006
+ Copyright Mission Critical Linux, 2000
+
+ 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.
+*/
+/** @file
+ *
+ * @author Lon H. Hohberger <lhh at redhat.com>
+ * @author Jeff Moyer <jmoyer at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "tcp.h"
+#include "debug.h"
+
+static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout);
+static int get_addr(const char *hostname, int family, struct sockaddr_storage *addr);
+
+/**
+ Set close-on-exec bit option for a socket.
+
+ @param fd Socket to set CLOEXEC flag
+ @return 0 on success, -1 on failure
+ @see fcntl
+ */
+static int
+set_cloexec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ flags |= FD_CLOEXEC;
+ return fcntl(fd, F_SETFD, flags);
+}
+
+
+/**
+ Bind to a port on the local IPv6 stack
+
+ @param addr_str Address to listen on, NULL for inaddr6_any
+ @param port Port to bind to
+ @param backlog same as backlog for listen(2)
+ @return 0 on success, -1 on failure
+ @see ipv4_bind
+ */
+int
+ipv6_listen(const char *addr_str, uint16_t port, int backlog)
+{
+ struct sockaddr_in6 _sin6;
+ int fd, opt=1;
+
+ dbg_printf(4, "%s: Setting up ipv6 listen socket for %s:%d\n",
+ __FUNCTION__, addr_str, port);
+
+ memset(&_sin6, 0, sizeof(_sin6));
+ _sin6.sin6_family = PF_INET6;
+ _sin6.sin6_port = htons(port);
+ _sin6.sin6_flowinfo = 0;
+
+ if (addr_str == NULL) {
+ _sin6.sin6_addr = in6addr_any;
+ } else {
+ struct sockaddr_storage ss;
+
+ if (get_addr(addr_str, AF_INET6, &ss) == -1) {
+ dbg_printf(4, "%s: Can't get addr for %s\n",
+ __FUNCTION__, addr_str);
+ return -1;
+ }
+
+ memcpy(&_sin6.sin6_addr,
+ &((struct sockaddr_in6 *)&ss)->sin6_addr, sizeof(_sin6.sin6_addr));
+ }
+
+ fd = socket(PF_INET6, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (set_cloexec(fd) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, (struct sockaddr *)&_sin6, sizeof(_sin6)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, backlog) < 0){
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+/**
+ Bind to a port on the local IPv4 stack
+
+ @param addr_str Address to listen on, NULL for inaddr_any
+ @param port Port to bind to
+ @param backlog same as backlog for listen(2)
+ @return 0 on success, -1 on failure
+ @see ipv6_bind
+ */
+int
+ipv4_listen(const char *addr_str, uint16_t port, int backlog)
+{
+ struct sockaddr_in _sin;
+ int fd, opt=1;
+
+ dbg_printf(4, "%s: Setting up ipv4 listen socket for %s:%d\n",
+ __FUNCTION__, addr_str, port);
+
+ _sin.sin_family = PF_INET;
+ _sin.sin_port = htons(port);
+
+ if (addr_str == NULL) {
+ _sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ } else {
+ struct sockaddr_storage ss;
+
+ if (get_addr(addr_str, AF_INET, &ss) == -1) {
+ dbg_printf(4, "%s: Can't get addr for %s\n",
+ __FUNCTION__, addr_str);
+ return -1;
+ }
+
+ memcpy(&_sin.sin_addr,
+ &((struct sockaddr_in *)&ss)->sin_addr, sizeof(_sin.sin_addr));
+ }
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (set_cloexec(fd) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, (struct sockaddr *)&_sin, sizeof(_sin)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, backlog) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+
+/**
+ Connect via ipv6 socket to a given IP address and port.
+
+ @param in6_addr IPv6 address to connect to
+ @param port Port to connect to
+ @param timeout Timeout, in seconds, to wait for a completed
+ connection
+ @return 0 on success, -1 on failure
+ @see connect_nb, ipv4_connect
+ */
+int
+ipv6_connect(struct in6_addr *in6_addr, uint16_t port, int timeout)
+{
+ struct sockaddr_in6 _sin6;
+ int fd, ret;
+
+ dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__);
+ fd = socket(PF_INET6, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&_sin6, 0, sizeof(_sin6));
+ _sin6.sin6_family = PF_INET6;
+ _sin6.sin6_port = htons(port);
+ _sin6.sin6_flowinfo = 0;
+ memcpy(&_sin6.sin6_addr, in6_addr, sizeof(_sin6.sin6_addr));
+
+ ret = connect_nb(fd, (struct sockaddr *)&_sin6, sizeof(_sin6), timeout);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+/**
+ Connect via ipv4 socket to a given IP address and port.
+
+ @param in_addr IPv4 address to connect to
+ @param port Port to connect to
+ @param timeout Timeout, in seconds, to wait for a completed
+ connection
+ @return 0 on success, -1 on failure
+ @see connect_nb, ipv6_connect
+ */
+int
+ipv4_connect(struct in_addr *in_addr, uint16_t port, int timeout)
+{
+ struct sockaddr_in _sin;
+ int fd, ret;
+
+ dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__);
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ _sin.sin_family = PF_INET;
+ _sin.sin_port = htons(port);
+ memcpy(&_sin.sin_addr, in_addr, sizeof(_sin.sin_addr));
+
+ ret = connect_nb(fd, (struct sockaddr *)&_sin, sizeof(_sin), timeout);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+/**
+ Connect in a non-blocking fashion to the designated address.
+
+ @param fd File descriptor to connect
+ @param dest sockaddr (ipv4 or ipv6) to connect to.
+ @param len Length of dest
+ @param timeout Timeout, in seconds, to wait for a completed
+ connection.
+ @return 0 on success, -1 on failure.
+ */
+static int
+connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout)
+{
+ int ret, flags = 1, err;
+ unsigned l;
+ fd_set rfds, wfds;
+ struct timeval tv;
+
+ /*
+ * Use TCP Keepalive
+ */
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags,
+ sizeof(flags))<0) {
+ return -1;
+ }
+
+ /*
+ Set up non-blocking connect
+ */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) {
+ return -1;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ return -1;
+ }
+
+ ret = connect(fd, dest, len);
+
+ if ((ret < 0) && (errno != EINPROGRESS))
+ return -1;
+
+ if (ret != 0) {
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ /* XXX check for -1 from select */
+
+ if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) {
+ l = sizeof(err);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR,
+ (void *)&err, &l) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (err != 0) {
+ close(fd);
+ errno = err;
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ close(fd);
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ errno = EIO;
+ return -1;
+}
+
+static int
+get_addr(const char *hostname, int family, struct sockaddr_storage *addr)
+{
+ struct addrinfo *res;
+ size_t len;
+ struct addrinfo ai;
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_family = family;
+
+ if (getaddrinfo(hostname, NULL, &ai, &res) != 0)
+ return -1;
+
+ switch (res->ai_addr->sa_family) {
+ case AF_INET:
+ len = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ goto out_fail;
+ }
+
+ if (len < (size_t) res->ai_addrlen)
+ goto out_fail;
+
+ memcpy(addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+
+ return 0;
+
+out_fail:
+ freeaddrinfo(res);
+ return -1;
+}
diff --git a/agents/virt/config/Makefile.am b/agents/virt/config/Makefile.am
new file mode 100644
index 0000000..19d9742
--- /dev/null
+++ b/agents/virt/config/Makefile.am
@@ -0,0 +1,44 @@
+###############################################################################
+###############################################################################
+##
+## 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
+
+EXTRA_DIST = config.l config.y fence_virt.conf
+
+noinst_LIBRARIES = libsimpleconfig.a
+
+libsimpleconfig_a_SOURCES = \
+ simpleconfig.c
+
+nodist_libsimpleconfig_a_SOURCES = \
+ y.tab.c \
+ config.c
+
+libsimpleconfig_a_CFLAGS = $(VIRT_AM_CFLAGS) $(AM_CFLAGS) -Wno-unused
+
+noinst_HEADERS = config-stack.h
+
+
+sysconf_DATA = fence_virt.conf
+
+# local rules
+y.tab.c: config.y
+ $(YACC) -d $^
+
+config.c: y.tab.c config.l
+ $(LEX) -oconfig.c $(srcdir)/config.l
+
+install-exec-hook:
+ chmod 600 $(DESTDIR)$(sysconfdir)/fence_virt.conf
+
+clean-local:
+ rm -f config.tab.c config.tab.h config.c y.tab.c y.tab.h
diff --git a/agents/virt/config/config-stack.h b/agents/virt/config/config-stack.h
new file mode 100644
index 0000000..1eb3cfa
--- /dev/null
+++ b/agents/virt/config/config-stack.h
@@ -0,0 +1,38 @@
+#ifndef _CONFIG_STACK_H
+#define _CONFIG_STACK_H
+
+int yyparse (void);
+extern FILE *yyin;
+
+struct value {
+ char *id;
+ char *val;
+ struct value *next;
+};
+
+
+struct node {
+ char *id;
+ char *val;
+ struct node *nodes;
+ struct value *values;
+ struct node *next;
+};
+
+
+struct parser_context {
+ struct value *val_list;
+ struct node *node_list;
+ struct parser_context *next;
+};
+
+extern struct value *val_list;
+extern struct node *node_list;
+extern struct parser_context *context_stack;
+
+int _sc_value_add(char *id, char *val, struct value **list);
+int _sc_node_add(char *id, char *val, struct value *vallist,
+ struct node *nodelist, struct node **list);
+
+
+#endif
diff --git a/agents/virt/config/config.l b/agents/virt/config/config.l
new file mode 100644
index 0000000..78462f3
--- /dev/null
+++ b/agents/virt/config/config.l
@@ -0,0 +1,106 @@
+%{
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include "config-stack.h"
+#include "y.tab.h"
+#include "simpleconfig.h"
+
+/* Some distributions can't use the output from flex without help */
+#define ECHO if(fwrite( yytext, yyleng, 1, yyout ))
+
+struct value *val_list = NULL;
+struct node *node_list = NULL;
+struct parser_context *context_stack = NULL;
+
+int _line_count = 1;
+
+%}
+%%
+[\n] {
+ ++_line_count;
+}
+
+[ \t]* {}
+
+\#[^\n]* {}
+
+"{" {
+ struct parser_context *c = NULL;
+ //printf("obrace\n");
+
+ c = malloc(sizeof(*c));
+ assert(c);
+
+ c->next = context_stack;
+ c->val_list = val_list;
+ c->node_list = node_list;
+
+ context_stack = c;
+ val_list = NULL;
+ node_list = NULL;
+
+ return T_OBRACE;
+}
+
+"}" {
+ return T_CBRACE;
+}
+
+";" {
+ return T_SEMI;
+}
+
+"=" {
+ return T_EQ;
+}
+
+[^ \t{};=\"\n]+ {
+ yylval.sval = strdup(yytext);
+ return T_ID;
+}
+
+\"[^\"]+\" {
+ yylval.sval = strdup(yytext+1);
+ yylval.sval[strlen(yytext)-2] = 0;
+ return T_VAL;
+}
+
+%%
+int
+yywrap(void)
+{
+ return 1;
+}
+
+
+#ifdef STANDALONE
+int
+main(int argc, char *argv[])
+{
+ char value[80];
+ config_object_t *c = NULL;
+
+ yyout = fopen("/dev/null","w");
+
+ c = sc_init();
+ sc_parse(c, NULL);
+ sc_dump(c, stdout);
+ if (argc == 2) {
+ if (sc_get(c, argv[1], value, sizeof(value)) == 0)
+ printf("%s = %s\n", argv[1], value);
+ else
+ printf("Not found\n");
+ } else if (argc == 3) {
+ printf("---------\n");
+ if (sc_set(c, argv[1], argv[2]) == 0)
+ sc_dump(c, stdout);
+ }
+
+ sc_release(c);
+
+ return 0;
+}
+#endif
diff --git a/agents/virt/config/config.y b/agents/virt/config/config.y
new file mode 100644
index 0000000..2ae0380
--- /dev/null
+++ b/agents/virt/config/config.y
@@ -0,0 +1,140 @@
+%{
+#include "config.h"
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+#include <assert.h>
+#include "config-stack.h"
+
+extern int yylex (void);
+int yyerror(const char *foo);
+
+int
+_sc_value_add(char *id, char *val, struct value **list)
+{
+ struct value *v;
+
+ v = malloc(sizeof(*v));
+ assert(v);
+
+ memset(v, 0, sizeof(*v));
+ v->id = id;
+ v->val = val;
+ //snprintf(v->id, sizeof(v->id), "%s", id);
+ //snprintf(v->val, sizeof(v->val), "%s", val);
+ //printf("add %s %s on to %p\n", id, val, *list);
+
+ v->next = *list;
+ *list = v;
+
+ //printf("new list %p\n", *list);
+ return 0;
+}
+
+
+int
+_sc_node_add(char *id, char *val, struct value *vallist,
+ struct node *nodelist, struct node **list)
+{
+ struct node *n;
+
+ n = malloc(sizeof(*n));
+ assert(n);
+
+ //printf("nodes %p values %p\n", nodelist, vallist);
+
+ memset(n, 0, sizeof(*n));
+ //snprintf(n->id, sizeof(n->id), "%s", id);
+ n->id = id; /* malloc'd during parsing */
+ n->val = val; /* malloc'd during parsing */
+ n->values = vallist;
+ n->nodes = nodelist;
+ n->next = *list;
+ *list = n;
+
+ return 0;
+}
+
+%}
+
+%token <sval> T_ID
+%token <sval> T_VAL
+%token T_OBRACE T_CBRACE T_EQ T_SEMI
+
+%start stuff
+
+%union {
+ char *sval;
+ int ival;
+}
+
+%%
+node:
+ T_ID T_OBRACE stuff T_CBRACE {
+ struct parser_context *c = NULL;
+
+ c = context_stack;
+ _sc_node_add($1, NULL, val_list, node_list, &c->node_list);
+ val_list = c->val_list;
+ node_list = c->node_list;
+ context_stack = c->next;
+
+ free(c);
+ }
+ |
+ T_ID T_EQ T_VAL T_OBRACE stuff T_CBRACE {
+ struct parser_context *c = NULL;
+
+ c = context_stack;
+ _sc_node_add($1, $3, val_list, node_list, &c->node_list);
+ val_list = c->val_list;
+ node_list = c->node_list;
+ context_stack = c->next;
+
+ free(c);
+ }
+ |
+ T_ID T_OBRACE T_CBRACE {
+ struct parser_context *c = NULL;
+
+ c = context_stack;
+ _sc_node_add($1, NULL, val_list, node_list, &c->node_list);
+ val_list = c->val_list;
+ node_list = c->node_list;
+ context_stack = c->next;
+
+ free(c);
+ }
+ |
+ T_ID T_EQ T_VAL T_OBRACE T_CBRACE {
+ struct parser_context *c = NULL;
+
+ c = context_stack;
+ _sc_node_add($1, $3, val_list, node_list, &c->node_list);
+ val_list = c->val_list;
+ node_list = c->node_list;
+ context_stack = c->next;
+
+ free(c);
+ }
+ ;
+
+stuff:
+ node stuff | assign stuff | node | assign
+ ;
+
+assign:
+ T_ID T_EQ T_VAL T_SEMI {
+ _sc_value_add($1, $3, &val_list);
+ }
+ ;
+%%
+
+extern int _line_count;
+
+int
+yyerror(const char *foo)
+{
+ printf("%s on line %d\n", foo, _line_count);
+ return 0;
+}
diff --git a/agents/virt/config/fence_virt.conf b/agents/virt/config/fence_virt.conf
new file mode 100644
index 0000000..03e2c58
--- /dev/null
+++ b/agents/virt/config/fence_virt.conf
@@ -0,0 +1,20 @@
+fence_virtd {
+ listener = "multicast";
+ backend = "libvirt";
+}
+
+listeners {
+ multicast {
+ key_file = "/etc/cluster/fence_xvm.key";
+ address = "225.0.0.12";
+ # Needed on Fedora systems
+ interface = "virbr0";
+ }
+}
+
+backends {
+ libvirt {
+ uri = "qemu:///system";
+ }
+}
+
diff --git a/agents/virt/config/simpleconfig.c b/agents/virt/config/simpleconfig.c
new file mode 100644
index 0000000..3315b32
--- /dev/null
+++ b/agents/virt/config/simpleconfig.c
@@ -0,0 +1,494 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "simpleconfig.h"
+#include "config-stack.h"
+#include "static_map.h"
+
+
+static pthread_mutex_t parser_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int
+print_value(struct value *v, int depth, FILE *fp)
+{
+ int x;
+
+ if (v->val == NULL)
+ return 0;
+
+ for (x = 0; x < depth; x++)
+ fprintf(fp, "\t");
+ fprintf(fp, "%s = \"%s\";\n", v->id, v->val);
+
+ return 0;
+}
+
+
+static void
+_sc_dump_d(struct node *node, int depth, FILE *fp)
+{
+ struct node *n;
+ struct value *v;
+ int x;
+
+ if (!node) {
+ //printf("Empty node\n");
+ return;
+ }
+
+ for (x = 0; x < depth; x++)
+ fprintf(fp, "\t");
+
+ if (node->val) {
+ fprintf(fp, "%s = \"%s\" {\n", node->id, node->val);
+ } else {
+ fprintf(fp, "%s {\n", node->id);
+ }
+
+ for (n = node->nodes; n; n = n->next) {
+ _sc_dump_d(n, depth+1, fp);
+ }
+
+ for (v = node->values; v; v = v->next) {
+ print_value(v, depth+1, fp);
+ }
+
+ for (x = 0; x < depth; x++)
+ fprintf(fp, "\t");
+ fprintf(fp, "}\n\n");
+}
+
+
+static void
+_sc_dump(void *config, FILE *fp)
+{
+ struct node *n, *node;
+ struct value *v, *values;
+
+ if (!config)
+ return;
+
+ values = ((struct parser_context *)config)->val_list;
+ node = ((struct parser_context *)config)->node_list;
+
+ for (n = node; n; n = n->next) {
+ _sc_dump_d(n, 0, fp);
+ }
+
+ for (v = values; v; v = v->next) {
+ print_value(v, 0, fp);
+ }
+}
+
+
+static int
+free_value(struct value *v)
+{
+ if (v) {
+ free(v->id);
+ free(v->val);
+ free(v);
+ }
+
+ return 0;
+}
+
+
+static void
+_sc_free_node(struct node *node)
+{
+ struct node *n;
+ struct value *v;
+
+ if (!node)
+ return;
+
+ while (node->nodes) {
+ n = node->nodes;
+ if (n) {
+ node->nodes = node->nodes->next;
+ _sc_free_node(n);
+ }
+ }
+
+ while (node->values) {
+ v = node->values;
+ node->values = node->values->next;
+ free_value(v);
+ }
+
+ free(node->id);
+ free(node);
+}
+
+
+static int
+_sc_free(void *config)
+{
+ struct node *n, *nlist;
+ struct value *v, *vlist;
+
+ if (!config)
+ return -1;
+
+ vlist = ((struct parser_context *)config)->val_list;
+ nlist = ((struct parser_context *)config)->node_list;
+
+ while (nlist) {
+ n = nlist;
+ nlist = nlist->next;
+ _sc_free_node(n);
+ }
+
+ ((struct parser_context *)config)->node_list = NULL;
+
+ while (vlist) {
+ v = vlist;
+ vlist = vlist->next;
+ free_value(v);
+ }
+
+ ((struct parser_context *)config)->val_list = NULL;
+
+ free(config);
+
+ return 0;
+}
+
+
+static int
+_sc_get(void *config, const char *key, char *value, size_t valuesz)
+{
+ char buf[1024];
+ struct node *n, *node;
+ struct value *v, *values;
+ char *ptr;
+ char *slash;
+ char *bracket;
+ char *id;
+ int req_index = 0;
+ int curr_index = 0;
+ int found;
+
+ if (!config)
+ return -1;
+
+ node = ((struct parser_context *)config)->node_list;
+ values = ((struct parser_context *)config)->val_list;
+
+ assert(strlen(key) < sizeof(buf));
+
+ ptr = (char *)key;
+
+ while ((slash = strchr(ptr, '/'))) {
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, ptr, (slash - ptr));
+ ptr = ++slash;
+
+ id = NULL;
+ bracket = strchr(buf, '[');
+ if (bracket) {
+ *bracket = 0;
+ ++bracket;
+
+ id = bracket;
+
+ bracket = strchr(bracket, ']');
+ if (!bracket)
+ return 1;
+ *bracket = 0;
+
+ if (id[0] == '@') {
+ ++id;
+ if (!strlen(id)) {
+ return 1;
+ }
+ } else {
+ req_index = atoi(id);
+ if (req_index <= 0)
+ return 1;
+ id = NULL;
+ }
+ }
+
+ found = 0;
+ curr_index = 0;
+
+ for (n = node; n; n = n->next) {
+
+ if (strcasecmp(n->id, buf))
+ continue;
+
+ ++curr_index;
+
+ if (req_index && (curr_index != req_index)) {
+ continue;
+ } else if (id && strcasecmp(n->val, id)) {
+ continue;
+ }
+
+ node = n->nodes;
+ values = n->values;
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ return 1;
+ }
+
+ if (ptr[0] != '@') {
+
+ strncpy(buf, ptr, sizeof(buf) - 1);
+ id = NULL;
+ bracket = strchr(buf, '[');
+ if (bracket) {
+ *bracket = 0;
+ ++bracket;
+
+ id = bracket;
+
+ bracket = strchr(bracket, ']');
+ if (!bracket)
+ return 1;
+ *bracket = 0;
+
+ if (id[0] == '@') {
+ ++id;
+ if (!strlen(id)) {
+ return 1;
+ }
+ } else {
+ req_index = atoi(id);
+ if (req_index <= 0)
+ return 1;
+ id = NULL;
+ }
+ }
+
+ found = 0;
+ curr_index = 0;
+
+ for (n = node; n; n = n->next) {
+
+ if (strcasecmp(n->id, buf))
+ continue;
+
+ ++curr_index;
+
+ if (req_index && (curr_index != req_index)) {
+ continue;
+ } else if (id && strcasecmp(n->val, id)) {
+ continue;
+ }
+ if (node->val) {
+ strncpy(value, node->val, valuesz);
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ ++ptr;
+ found = 0;
+ id = NULL;
+
+ strncpy(buf, ptr, sizeof(buf) - 1);
+ bracket = strchr(buf, '[');
+
+ req_index = 0;
+ curr_index = 0;
+
+ if (bracket) {
+ *bracket = 0;
+ ++bracket;
+
+ id = bracket;
+
+ bracket = strchr(bracket, ']');
+ if (!bracket)
+ return 1;
+ *bracket = 0;
+
+ req_index = atoi(id);
+ if (req_index <= 0)
+ return 1;
+ id = NULL;
+ }
+
+ for (v = values; v; v = v->next) {
+
+ if (strcasecmp(v->id, buf))
+ continue;
+
+ ++curr_index;
+ if (req_index && (curr_index != req_index))
+ continue;
+ snprintf(value, valuesz, "%s", v->val);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int
+_sc_set(void *config, const char *key, const char *value)
+{
+ char buf[1024];
+ struct node *n, **nodes = &((struct parser_context *)config)->node_list;
+ struct value *v, **values = &((struct parser_context *)config)->val_list;
+ char *ptr;
+ char *slash;
+ char *id_dup, *val_dup;
+ int found = 0;
+
+ ptr = (char *)key;
+ while ((slash = strchr(ptr, '/'))) {
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, ptr, (slash - ptr));
+ ptr = ++slash;
+ found = 0;
+
+ for (n = *nodes; n; n = n->next) {
+ if (strcasecmp(n->id, buf))
+ continue;
+
+ nodes = &n->nodes;
+ values = &n->values;
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ id_dup = strdup(buf);
+ if (!id_dup)
+ return -1;
+ _sc_node_add(id_dup, NULL, NULL, NULL, nodes);
+ n = *nodes;
+ nodes = &n->nodes;
+ values = &n->values;
+ }
+ }
+
+ if (ptr[0] != '@')
+ return 1;
+ ++ptr;
+
+ for (v = *values; v; v = v->next) {
+ if (strcasecmp(v->id, ptr))
+ continue;
+
+ ptr = v->val;
+ if (value) {
+ v->val = strdup(value);
+ if (!v->val) {
+ v->val = ptr;
+ return -1;
+ }
+ } else {
+ v->val = NULL;
+ }
+ free(ptr);
+
+ return 0;
+ }
+
+ if (!value)
+ return 0;
+
+ id_dup = strdup(ptr);
+ if (!id_dup)
+ return -1;
+
+ val_dup = strdup(value);
+ if (!val_dup) {
+ free(id_dup);
+ return -1;
+ }
+ _sc_value_add(id_dup, val_dup, values);
+
+ return 0;
+}
+
+
+static int
+_sc_parse(const char *filename, void **config)
+{
+ struct parser_context *c;
+ FILE *fp = NULL;
+ int ret = 0;
+
+ if (!config)
+ return -1;
+
+ pthread_mutex_lock(&parser_mutex);
+ if (filename) {
+ fp = fopen(filename, "r");
+ yyin = fp;
+ if (fp)
+ ret = yyparse();
+ else
+ ret = 1;
+ } else {
+ ret = 1;
+ }
+
+ c = malloc(sizeof(*c));
+ if (!c) {
+ ret = -1;
+ goto out_unlock;
+ }
+ c->node_list = node_list;
+ c->val_list = val_list;
+ c->next = NULL;
+ val_list = NULL;
+ node_list = NULL;
+ *config = (void *)c;
+
+ if (fp)
+ fclose(fp);
+
+out_unlock:
+ pthread_mutex_unlock(&parser_mutex);
+ return ret;
+}
+
+
+static const config_object_t sc_object = {
+ .get = _sc_get,
+ .set = _sc_set,
+ .parse = _sc_parse,
+ .free = _sc_free,
+ .dump = _sc_dump,
+ .info = NULL
+};
+
+
+config_object_t *
+sc_init(void)
+{
+ config_object_t *o;
+
+ o = malloc(sizeof(*o));
+ if (!o)
+ return NULL;
+ memset(o, 0, sizeof(*o));
+ memcpy(o, &sc_object, sizeof(*o));
+
+ return o;
+}
+
+
+void
+sc_release(config_object_t *c)
+{
+ sc_free(c);
+ free(c);
+}
diff --git a/agents/virt/docs/README b/agents/virt/docs/README
new file mode 100644
index 0000000..e2b19bc
--- /dev/null
+++ b/agents/virt/docs/README
@@ -0,0 +1,125 @@
+TODO: update
+
+I. Fence_xvm - Virtual machine fencing agent
+
+Fence_xvm is an agent which establishes a communications link between
+a cluster of virtual machines (VC) and a cluster of domain0/physical
+nodes which are hosting the virtual cluster. Its operations are
+fairly simple.
+
+ (a) Start a listener service.
+ (b) Send a multicast packet requesting that a VM be fenced.
+ (c) Authenticate client.
+ (e) Read response.
+ (f) Exit with success/failure, depending on the response received.
+
+If any of the above steps fail, the fencing agent exits with a failure
+code and fencing is retried by the virtual cluster at a later time.
+Because of the simplicty of fence_xvm, it is not necessary that
+fence_xvm be run from within a virtualized guest - all it needs is
+libnspr and libnss and a shared private key (for authentication; we
+would hate to receive a false positive response from a node not in the
+cluster!).
+
+
+II. Fence_virtd - Virtual machine fencing host
+
+Fence_virtd is a daemon which runs on physical hosts (e.g. in domain0)
+of the cluster hosting the virtual cluster. It listens on a port
+for multicast traffic from virtual cluster(s), and takes actions.
+Multiple disjoint virtual clusters can coexist on a single physical
+host cluster, but this requires multiple instances of fence_virtd.
+
+NOTE: fence_virtd *MUST* be run on ALL nodes in a given cluster which
+will be hosting virtual machines if fence_xvm is to be used for
+fencing!
+
+There are a couple of ways the multicast packet is handled,
+depending on the state of the host OS. It might be hosting the VM,
+or it might not. Furthermore, the VM might "reside" on a host which
+has failed.
+
+In order to be able to guarantee safe fencing of a VM even if the
+last- known host is down, we must store the last-known locations of
+each virtual machine in some sort of cluster-wide way. For this, we
+use the corosync CPG API. Every few seconds, fence_virtd queries the
+hypervisor via libvirt and stores any local VM states and sends those
+states over CPG to all other members. In the event of a physical node
+failure (which consequently causes the failure of one or more guests),
+we can then read the stored VM state corresponding to the guest we need
+to fence to find out the previous owner. With that information, we can
+infer if the known host node has been fenced. If so, then the VM is clean
+as well. The physical cluster must, therefore, have fencing in order for
+fence_virtd to work.
+
+Operation of a node hosting a VM which needs to be fenced:
+
+ (a) Receive multicast packet
+ (b) Authenticate multicast packet
+ (c) Open connection to host contained within multicast
+ packet.
+ (d) Authenticate server.
+ (e) Carry out fencing operation (e.g. call libvirt to destroy or
+ reboot the VM; there is no "on" method at this point).
+ (f) If operation succeeds, send success response.
+
+Operation of high-node-ID:
+
+ (a) Receive multicast packet
+ (b) Authenticate multicast packet
+ (c) Read VM state from stored CPG messages
+ (d) Check liveliness of nodeID hosting VM (if alive, do nothing)
+ (e) Open connection to host contained within multicast
+ packet.
+ (f) Check with CMAN to see if last-known host has been fenced.
+ (g) If last-known host has been fenced, send success response.
+ (h) Authenticate server & send response.
+
+NOTE: There is always a possibility that a VM is started again
+before the fencing operation and CPG update for that VM
+occurs. If the VM has booted and rejoined the cluster, fencing will
+not be necessary. If it is in the process of booting, but has not
+yet joined the cluster, fencing will also not be necessary - because
+it will not be using cluster resources yet.
+
+
+III. Security considerations
+
+While fencing is generally expected to run on a more or less trusted
+network, there are cases where it may not be.
+
+* The multicast packet is subject to replay attacks, but because no
+fencing action is taken based solely on the information contained
+within the packet, this should not allow an attacker to maliciously
+fence a VM from outside the cluster, though it may be possible to
+cause a DoS of fence_virtd if enough multicast packets are sent.
+
+* The only currently supported authentication mechanisms are simple
+challenge-response based on a shared private key and pseudorandom
+number generation.
+
+* An attacker with access to the shared key(s) can easily fence any
+known VM, even if they are not on a cluster node.
+
+* Different shared keys should be used for different virtual
+clusters on the same subnet (whether in the same physical cluster
+or not). Additionally, multiple fence_virtd instances must be run
+(each listening on a different multicast IP + port combination).
+
+IV. Configuration
+
+Generate a random key file. An example of how to generate it is:
+
+ dd if=/dev/urandom of=/etc/cluster/fence_xvm.key bs=4096 count=1
+
+Distribute the generated key file to all domUs in a cluster as well
+as all dom0s which will be hosting that particular cluster of domUs.
+The key should not be placed on shared file systems (because shared
+file systems require the cluster, which requires fencing...).
+
+Start fence_virtd on all hosts
+
+Configure fence_xvm on the domU cluster...
+
+rest...tbd
+
diff --git a/agents/virt/docs/TODO b/agents/virt/docs/TODO
new file mode 100644
index 0000000..17456cf
--- /dev/null
+++ b/agents/virt/docs/TODO
@@ -0,0 +1,7 @@
+High Priority / Blockers for v1.0;
+
+* endian-clean / 64-bit clean data structure analysis
+
+Future Stuff:
+
+* clean up development bits so third parties can develop plugins
diff --git a/agents/virt/docs/architecture.txt b/agents/virt/docs/architecture.txt
new file mode 100644
index 0000000..54fda11
--- /dev/null
+++ b/agents/virt/docs/architecture.txt
@@ -0,0 +1,16 @@
+The actual architecture of fence_virtd is very simple. We have a set
+of listener plugins which listens for fencing requests for virtual
+machines.
+
+These plugins are assigned callbacks which are entry functions in to
+the backend plugins. The backend plugins perform the actual fencing
+request.
+
+In the middle, we have only enough code to provide basic integration
+functions between the listener and backend plugins. This includes a
+very simple confiugration plugin which we pass to each of the plugins.
+
+Because we are passing function pointers in to the plugins themselves
+for configuration (rather than having the plugins call an API directly,
+for example), we are able to swap out the configuration subsystem for
+other, more full-featured configuration systems, such as libccs.
diff --git a/agents/virt/docs/fence_virt.txt b/agents/virt/docs/fence_virt.txt
new file mode 100644
index 0000000..e554ce4
--- /dev/null
+++ b/agents/virt/docs/fence_virt.txt
@@ -0,0 +1,127 @@
+We need a fencing agent which can work in a variety of guest cluster
+configurations and host configurations.
+
+Requirements
+
+1. Nonrequirement of guest to host networking. Virtual machines
+ may be configured to run using a nework unknown to the host
+ operating system. Therefore, the ability to run without network
+ communication between the guest and the hsot is required.
+
+2. Ease of configuration. The absolute minimum possible configuration
+ must be available.
+
+3. Nonrequirement of host clustering software. Multiple layers of
+ configuration sucks. While I fundamentally disagree with the general
+ idea that running CMAN on the host constitutes a "heavyweight
+ cluster", perception is important.
+
+4. Ability to support RHEV-M, oVirt server, and other virtual machine
+ management technologies. This is beneficial from a security standpoint
+ since it is assumed the management server will be aware of what VMs
+ are allowed to fence what other VMs.
+
+5. Upgrade compatibility with fence_xvm from a configuration standpoint.
+ This may be provided by a symlink over fence_xvm. If this feature
+ can not be provided as a matter of design, a method to convert an
+ existing fence_xvm/fence_xvmd configuration to fence_virt must be
+ present.
+
+
+Guest to Host Interaction
+-------------------------
+
+The proposal is to use various communications media plugins in order
+to facilitate flexibility with respect to how virtual machine
+environments are configured.
+
+There are at least 3 simple plugins for guest/client to host/server
+communications:
+
+ * Direct serial. The guest sends fencing requests out via /dev/ttySX
+ in the guest. The host is listening on a Unix domain socket[1],
+ and forwards fencing requests accordingly.
+
+ This satisifies most of the requirements, but adds a conundrum
+ when configuring guest clusters, as /dev/ttySX may be /dev/ttySY
+ on another guest. So, either we must account for this per-guest
+ configuration discrepancy or we must make it an administrative
+ requirement to provide the same serial device on each host
+
+ * Multicast. This violates the networking requirement, but this is
+ okay since this method of operation is optional. This operational
+ mode provides for one of the simpler configurations: all that is
+ needed is the guest's name or UUID. The guest to host
+ communications operates in the same manner as fence_xvm/fence_xvmd,
+ except that there is an implied requirement on restricting the
+ multicast packets accepted to be from the local guests.
+
+ * VM Channel over Serial. This works like direct serial, but
+ instead of owning the whole device, the device may be shared between
+ multiple applications. The server subscribes to a channel and
+ listens for fencing requests on the channel; the client in the
+ guest OS connects to the channel and issues fencing requests across
+ it. One interesting thing is that it may be possible to provide
+ unprivileged users the ability to fence using this method (I
+ do not claim to know if this is useful or not).
+
+
+Host to Hypervisor interaction
+------------------------------
+
+Similar to the way we have plugins for guest to host interaction,
+we also have plugins which actually do the real work. These plugins
+are responsible for all of the actual real work performed, including
+tracking VMs if required, forwarding requests to the appropriate hosts
+or management services, and handling the responses.
+
+We propose at 5 plugins in this case:
+
+ * Libvirt (local-only). There is no intracommunication and no
+ migration support is provided
+
+ * Cluster CPG (+ libvirt). This the way fence_xvmd
+ operates today. This setup has the most requirements on the
+ infrastructure, as it requires guest to host networking _and_
+ host-to-host clustering in order to keep track of virtual
+ machines. The benefit is that it is self-contained and requires
+ no external management nodes. VM states are stored so that other
+ CPG group members know the locations of other VMs and can make
+ some decisions about whether a VM is dead based on whether a host
+ is dead (i.e. if fencing is in use or can be performed on the
+ host).
+
+ * Libvirt-QMF ... ??? Subscription to the appropriate cluster
+ specific AMQP channel is required on the host side, but this
+ handles routing the message very easily. The fencing request
+ is forwarded to the other listeners on the channel, the VM owner
+ takes the action requested and returns a value. When new VMs
+ are created, the event is broadcast out via the AMQP channel so
+ other hosts know the locations of other VMs and can make some
+ decisions about whether a VM is dead based on whether a host
+ is dead (i.e. if fencing is in use or can be performed on the
+ host).
+
+ * oVirt Manager. The request is forwarded to the oVirt Manager
+ and the oVirt manager is responsible for taking the appropriate
+ action and responding to the request.
+
+ * RHEV-M. The request is forwarded to the RHEV-M node, which is
+ responsible for taking the appropriate action and responding to
+ the request.
+
+
+These plugins have no requirements on which guest to host communication
+plugin is used (you could, if you wanted, use 'direct serial' with
+'cluster cpg', or 'multicast' with 'RHEV-H' for example).
+
+These plugins must also be able to discover where appropriate. For
+example, the cpg plugin can only be used if corosync/openais
+is running. A defined plugin preference order should be specified/documented
+so that the host daemon behaves in a predictable manner in absence of
+host-side configuration data (about which plugin to use).
+
+
+[1] TCP was also explored, however, the security is much better
+ using a Unix domain socket, despite the additional complexity
+ of listening for VM creation events.
diff --git a/agents/virt/fence_virtd.service.in b/agents/virt/fence_virtd.service.in
new file mode 100644
index 0000000..9d2836f
--- /dev/null
+++ b/agents/virt/fence_virtd.service.in
@@ -0,0 +1,23 @@
+[Unit]
+Description=Fence-Virt system host daemon
+Documentation=man:fence_virtd(8)
+Documentation=man:fence_virt.conf(5)
+
+After=basic.target
+After=network.target
+After=syslog.target
+After=libvirtd.service
+After=corosync.service
+
+Requires=basic.target
+Requires=network.target
+
+[Install]
+WantedBy=multi-user.target
+
+[Service]
+Type=forking
+Restart=on-failure
+Environment="FENCE_VIRTD_ARGS=-w"
+
+# Autogenerated below here
diff --git a/agents/virt/include/bcast.h b/agents/virt/include/bcast.h
new file mode 100644
index 0000000..5113f04
--- /dev/null
+++ b/agents/virt/include/bcast.h
@@ -0,0 +1,16 @@
+#ifndef _XVM_MCAST_H
+#define _XVM_MCAST_H
+
+#define IPV4_MCAST_DEFAULT "225.0.0.12"
+#define IPV6_MCAST_DEFAULT "ff05::3:1"
+
+int ipv4_recv_sk(char *addr, int port);
+int ipv4_send_sk(char *src_addr, char *addr, int port,
+ struct sockaddr *src, socklen_t slen,
+ int ttl);
+int ipv6_recv_sk(char *addr, int port);
+int ipv6_send_sk(char *src_addr, char *addr, int port,
+ struct sockaddr *src, socklen_t slen,
+ int ttl);
+
+#endif
diff --git a/agents/virt/include/client.h b/agents/virt/include/client.h
new file mode 100644
index 0000000..48b92c3
--- /dev/null
+++ b/agents/virt/include/client.h
@@ -0,0 +1,9 @@
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+int tcp_fence_virt(fence_virt_args_t *args);
+int serial_fence_virt(fence_virt_args_t *args);
+int mcast_fence_virt(fence_virt_args_t *args);
+int vsock_fence_virt(fence_virt_args_t *args);
+void do_read_hostlist(int fd, int timeout);
+#endif
diff --git a/agents/virt/include/debug.h b/agents/virt/include/debug.h
new file mode 100644
index 0000000..d6a5416
--- /dev/null
+++ b/agents/virt/include/debug.h
@@ -0,0 +1,31 @@
+/*
+ Copyright Red Hat, Inc. 2007
+
+ 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.
+*/
+#ifndef _DBG_H
+#define _DBG_H
+
+void dset(int);
+int dget(void);
+
+#define dbg_printf(level, fmt, args...) \
+do { \
+ if (dget()>=level) \
+ printf(fmt, ##args); \
+} while(0)
+
+#endif
diff --git a/agents/virt/include/fdops.h b/agents/virt/include/fdops.h
new file mode 100644
index 0000000..f3194de
--- /dev/null
+++ b/agents/virt/include/fdops.h
@@ -0,0 +1,14 @@
+#ifndef _FDOPS_H
+#define _FDOPS_H
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int _select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds,
+ struct timeval *timeout);
+ssize_t _write_retry(int fd, void *buf, int count, struct timeval * timeout);
+ssize_t _read_retry(int sockfd, void *buf, int count,
+ struct timeval * timeout);
+
+#endif
diff --git a/agents/virt/include/history.h b/agents/virt/include/history.h
new file mode 100644
index 0000000..3d28518
--- /dev/null
+++ b/agents/virt/include/history.h
@@ -0,0 +1,25 @@
+#ifndef _HISTORY_H
+#define _HISTORY_H
+
+typedef struct _history_node {
+ list_head();
+ void *data;
+ time_t when;
+} history_node;
+
+typedef int (*history_compare_fn)(void *, void *);
+
+typedef struct _history_info {
+ history_node *hist;
+ history_compare_fn compare_func;
+ time_t timeout;
+ size_t element_size;
+} history_info_t;
+
+history_info_t *history_init(history_compare_fn func,
+ time_t expiration, size_t element_size);
+int history_check(history_info_t *hinfo, void *stuff);
+int history_record(history_info_t *hinfo, void *data);
+int history_wipe(history_info_t *hinfo);
+
+#endif
diff --git a/agents/virt/include/ip_lookup.h b/agents/virt/include/ip_lookup.h
new file mode 100644
index 0000000..3386c71
--- /dev/null
+++ b/agents/virt/include/ip_lookup.h
@@ -0,0 +1,40 @@
+/*
+ Copyright Red Hat, Inc. 2004,2006
+
+ The Magma Cluster API Library is free software; you can redistribute
+ it and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either version
+ 2.1 of the License, or (at your option) any later version.
+
+ The Magma Cluster API Library is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+*/
+/** @file
+ * Header for ip_lookup.c
+ */
+#ifndef _IP_LOOKUP_H
+#define _IP_LOOKUP_H
+
+#include <sys/queue.h>
+
+typedef struct _ip_address {
+ TAILQ_ENTRY(_ip_address) ipa_entries;
+ char ipa_family;
+ char *ipa_address;
+} ip_addr_t;
+
+typedef TAILQ_HEAD(_ip_list, _ip_address) ip_list_t;
+
+int ip_search(ip_list_t *ipl, char *ip_name);
+int ip_free_list(ip_list_t *ipl);
+int ip_build_list(ip_list_t *ipl);
+int ip_lookup(char *, struct addrinfo **);
+
+#endif
diff --git a/agents/virt/include/list.h b/agents/virt/include/list.h
new file mode 100644
index 0000000..01340d6
--- /dev/null
+++ b/agents/virt/include/list.h
@@ -0,0 +1,84 @@
+#ifndef _LIST_H
+#define _LIST_H
+
+/**
+ Simple list handlig macros.
+ Needs rewrite or inclusion of /usr/include/linux/list.h as a replacement.
+ */
+
+/* Must be first if structure is going to use it. */
+struct list_entry {
+ struct list_entry *le_next, *le_prev;
+};
+
+#define list_head() struct list_entry _list_head
+
+#define le(p) (&((*p)._list_head))
+
+#define list_insert(list, newnode) \
+do { \
+ if (!(*list)) { \
+ le(newnode)->le_next = \
+ le(newnode)->le_prev = le(newnode); \
+ *list = (void *)le(newnode); \
+ } else { \
+ le(*list)->le_prev->le_next = le(newnode); \
+ le(newnode)->le_next = le(*list); \
+ le(newnode)->le_prev = le(*list)->le_prev; \
+ le(*list)->le_prev = le(newnode); \
+ } \
+} while (0)
+
+
+#define list_prepend(list, newnode) \
+do { \
+ list_insert(list, newnode); \
+ *list = newnode; \
+} while (0)
+
+
+#define list_remove(list, oldnode) \
+do { \
+ if (le(oldnode) == le(*list)) { \
+ *list = (void *)le(*list)->le_next; \
+ } \
+ if (le(oldnode) == le(*list)) { \
+ le(oldnode)->le_next = NULL; \
+ le(oldnode)->le_prev = NULL; \
+ *list = NULL; \
+ } else { \
+ le(oldnode)->le_next->le_prev = le(oldnode)->le_prev; \
+ le(oldnode)->le_prev->le_next = le(oldnode)->le_next; \
+ le(oldnode)->le_prev = NULL; \
+ le(oldnode)->le_next = NULL; \
+ } \
+} while (0)
+
+
+/*
+ * list_for(list, tmp, counter) {
+ * stuff;
+ * }
+ *
+ * counter = # of items in list when done.
+ * * sets cnt to 0 before even checking list;
+ * * checks for valid list
+ * * traverses list, incrementing counter. If we get to the for loop,
+ * there must be at least one item in the list
+ * * cnt ends up being the number of items in the list.
+ */
+#define list_for(list, curr, cnt) \
+ if ((!(cnt=0)) && (*list != NULL)) \
+ for (curr = *list; \
+ (cnt == 0) || (curr != *list); \
+ curr = (void*)le(curr)->le_next, \
+ cnt++)
+
+#define list_for_rev(list, curr, cnt) \
+ if ((!(cnt=0)) && (*list != NULL)) \
+ for (curr = (void *)(le(*list)->le_prev); \
+ (cnt == 0) || ((void *)curr != le(*list)->le_prev); \
+ curr = (void*)(le(curr)->le_prev), \
+ cnt++)
+
+#endif
diff --git a/agents/virt/include/mcast.h b/agents/virt/include/mcast.h
new file mode 100644
index 0000000..f86ad96
--- /dev/null
+++ b/agents/virt/include/mcast.h
@@ -0,0 +1,32 @@
+/*
+ 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.
+*/
+#ifndef _XVM_MCAST_H
+#define _XVM_MCAST_H
+
+#define IPV4_MCAST_DEFAULT "225.0.0.12"
+#define IPV6_MCAST_DEFAULT "ff05::3:1"
+
+int ipv4_recv_sk(char *addr, int port, unsigned int ifindex);
+int ipv4_send_sk(char *src_addr, char *addr, int port,
+ struct sockaddr *src, socklen_t slen);
+int ipv6_recv_sk(char *addr, int port, unsigned int ifindex);
+int ipv6_send_sk(char *src_addr, char *addr, int port,
+ struct sockaddr *src, socklen_t slen);
+
+#endif
diff --git a/agents/virt/include/options.h b/agents/virt/include/options.h
new file mode 100644
index 0000000..6e2c1c1
--- /dev/null
+++ b/agents/virt/include/options.h
@@ -0,0 +1,99 @@
+/*
+ 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.
+*/
+#ifndef _XVM_OPTIONS_H
+#define _XVM_OPTIONS_H
+
+typedef enum {
+ F_FOREGROUND = 0x1,
+ F_NOCCS = 0x2,
+ F_ERR = 0x4,
+ F_HELP = 0x8,
+ F_USE_UUID = 0x10,
+ F_VERSION = 0x20,
+ F_CCSERR = 0x40,
+ F_CCSFAIL = 0x80,
+ F_NOCLUSTER = 0x100
+} arg_flags_t;
+
+typedef enum {
+ MODE_MULTICAST = 0,
+ /*MODE_BROADCAST = 1,*/
+ MODE_SERIAL = 2,
+ MODE_VMCHANNEL = 3,
+ MODE_TCP = 4,
+ MODE_VSOCK = 5
+} client_mode_t;
+
+typedef struct {
+ char *domain;
+ fence_cmd_t op;
+ client_mode_t mode;
+ int debug;
+ int timeout;
+ int delay;
+ int retr_time;
+ arg_flags_t flags;
+
+ struct network_args {
+ char *addr;
+ char *ipaddr;
+ char *key_file;
+ uint32_t cid;
+ int port;
+ fence_hash_t hash;
+ fence_auth_type_t auth;
+ int family;
+ unsigned int ifindex;
+ } net;
+
+ struct serial_args {
+ char *device; /* Serial device */
+ char *speed;
+ char *address; /* vmchannel IP */
+ } serial;
+} fence_virt_args_t;
+
+/* Private structure for commandline / stdin fencing args */
+struct arg_info {
+ const char opt;
+ const char *opt_desc;
+ const char *stdin_opt;
+ const char *obsoletes;
+ int deprecated;
+ int eh;
+ const char *content_type;
+ const char *default_value;
+ const char *desc;
+ void (*assign)(fence_virt_args_t *, struct arg_info *, char *);
+};
+
+
+/* Get options */
+void args_init(fence_virt_args_t *args);
+void args_finalize(fence_virt_args_t *args);
+
+void args_get_getopt(int argc, char **argv, const char *optstr,
+ fence_virt_args_t *args);
+void args_get_stdin(const char *optstr, fence_virt_args_t *args);
+void args_get_ccs(const char *optstr, fence_virt_args_t *args);
+void args_usage(char *progname, const char *optstr, int print_stdin);
+void args_print(fence_virt_args_t *args);
+void args_metadata(char *progname, const char *optstr);
+
+#endif
diff --git a/agents/virt/include/server_plugin.h b/agents/virt/include/server_plugin.h
new file mode 100644
index 0000000..04ceae0
--- /dev/null
+++ b/agents/virt/include/server_plugin.h
@@ -0,0 +1,130 @@
+/* */
+
+#ifndef _SERVER_PLUGIN_H
+#define _SERVER_PLUGIN_H
+
+#include "config.h"
+
+#define PLUGIN_VERSION_LISTENER ((double)0.3)
+#define PLUGIN_VERSION_BACKEND ((double)0.2)
+
+typedef void * listener_context_t;
+typedef void * backend_context_t;
+
+/* These callbacks hand requests off to the
+ appropriate backend. */
+
+/* Do nothing. Returns 1 (failure) to caller */
+typedef int (*fence_null_callback)(const char *vm_name,
+ void *priv);
+
+/* Turn the VM 'off'. Returns 0 to caller if successful or
+ nonzero if unsuccessful. */
+typedef int (*fence_off_callback)(const char *vm_name, const char *src,
+ uint32_t seqno, void *priv);
+
+/* Turn the VM 'on'. Returns 0 to caller if successful or
+ nonzero if unsuccessful. */
+typedef int (*fence_on_callback)(const char *vm_name, const char *src,
+ uint32_t seqno, void *priv);
+
+/* Reboot a VM. Returns 0 to caller if successful or
+ nonzero if unsuccessful. */
+typedef int (*fence_reboot_callback)(const char *vm_name, const char *src,
+ uint32_t seqno, void *priv);
+
+/* Get status of a VM. Returns 0 to caller if VM is alive or
+ nonzero if VM is not alive. */
+typedef int (*fence_status_callback)(const char *vm_name,
+ void *priv);
+
+/* Get status of backend. Returns 0 to caller if backend
+ is responding to requests. */
+typedef int (*fence_devstatus_callback)(void *priv);
+
+
+/* VMs available to fence. Returns 0 to caller if backend
+ is responding to requests and a host list can be produced */
+typedef int (*hostlist_callback)(const char *vm_name, const char *uuid,
+ int state, void *arg);
+typedef int (*fence_hostlist_callback)(hostlist_callback cb,
+ void *arg, void *priv);
+
+typedef int (*backend_init_fn)(backend_context_t *c,
+ config_object_t *config);
+typedef int (*backend_cleanup_fn)(backend_context_t c);
+
+typedef struct _fence_callbacks {
+ fence_null_callback null;
+ fence_off_callback off;
+ fence_on_callback on;
+ fence_reboot_callback reboot;
+ fence_status_callback status;
+ fence_devstatus_callback devstatus;
+ fence_hostlist_callback hostlist;
+} fence_callbacks_t;
+
+typedef struct backend_plugin {
+ const char *name;
+ const char *version;
+ const fence_callbacks_t *callbacks;
+ backend_init_fn init;
+ backend_cleanup_fn cleanup;
+} backend_plugin_t;
+
+double backend_plugin_version(void);
+const backend_plugin_t * backend_plugin_info(void);
+
+#define BACKEND_VER_SYM backend_plugin_version
+#define BACKEND_INFO_SYM backend_plugin_info
+#define BACKEND_VER_STR "backend_plugin_version"
+#define BACKEND_INFO_STR "backend_plugin_info"
+
+typedef int (*listener_init_fn)(listener_context_t *c,
+ const fence_callbacks_t *cb,
+ config_object_t *config,
+ map_object_t *map,
+ void *priv);
+typedef int (*listener_dispatch_fn)(listener_context_t c,
+ struct timeval *timeout);
+typedef int (*listener_cleanup_fn)(listener_context_t c);
+
+
+typedef struct listener_plugin {
+ const char *name;
+ const char *version;
+ listener_init_fn init;
+ listener_dispatch_fn dispatch;
+ listener_cleanup_fn cleanup;
+} listener_plugin_t;
+
+double listener_plugin_version(void);
+const listener_plugin_t * listener_plugin_info(void);
+
+#define LISTENER_VER_SYM listener_plugin_version
+#define LISTENER_INFO_SYM listener_plugin_info
+#define LISTENER_VER_STR "listener_plugin_version"
+#define LISTENER_INFO_STR "listener_plugin_info"
+
+typedef enum {
+ PLUGIN_NONE = 0,
+ PLUGIN_LISTENER = 1,
+ PLUGIN_BACKEND = 2
+} plugin_type_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const backend_plugin_t *plugin_find_backend(const char *name);
+const listener_plugin_t *plugin_find_listener(const char *name);
+
+void plugin_dump(void);
+int plugin_load(const char *filename);
+void plugin_unload(void);
+int plugin_search(const char *pathname);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/agents/virt/include/simple_auth.h b/agents/virt/include/simple_auth.h
new file mode 100644
index 0000000..4cd57ed
--- /dev/null
+++ b/agents/virt/include/simple_auth.h
@@ -0,0 +1,35 @@
+/*
+ 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.
+*/
+#ifndef _XVM_SIMPLE_AUTH_H
+#define _XVM_SIMPLE_AUTH_H
+
+#include <sys/types.h>
+
+/* 2-way challenge/response simple auth */
+#define DEFAULT_KEY_FILE "/etc/cluster/fence_xvm.key"
+
+int read_key_file(char *, char *, size_t);
+int sock_challenge(int, fence_auth_type_t, void *, size_t, int);
+int sock_response(int, fence_auth_type_t, void *, size_t, int);
+int sign_request(fence_req_t *, void *, size_t);
+int verify_request(fence_req_t *, fence_hash_t, void *, size_t);
+
+/* SSL certificate-based authentication TBD */
+
+#endif
diff --git a/agents/virt/include/simpleconfig.h b/agents/virt/include/simpleconfig.h
new file mode 100644
index 0000000..6aba85f
--- /dev/null
+++ b/agents/virt/include/simpleconfig.h
@@ -0,0 +1,56 @@
+#ifndef _SIMPLECONFIG_H
+#define _SIMPLECONFIG_H
+
+typedef int (*config_get_t)(void *config, const char *key,
+ char *value, size_t valuesz);
+typedef int (*config_set_t)(void *config, const char *key,
+ const char *value);
+typedef int (*config_parse_t)(const char *filename, void **config);
+typedef int (*config_free_t)(void *config);
+typedef void (*config_dump_t)(void *config, FILE *fp);
+
+/*
+ * We use an abstract object here so we do not have to link loadable
+ * modules against the configuration library.
+ */
+
+typedef struct {
+ config_get_t get;
+ config_set_t set;
+ config_parse_t parse;
+ config_free_t free;
+ config_dump_t dump;
+ void *info;
+} config_object_t;
+
+/*
+ * These macros may be called from within a loadable module
+ */
+#define sc_get(obj, key, value, valuesz) \
+ obj->get(obj->info, key, value, valuesz)
+#define sc_set(obj, key, value) \
+ obj->set(obj->info, key, value)
+#define sc_parse(obj, filename) \
+ obj->parse(filename, &obj->info)
+#define sc_free(obj) \
+ obj->free(obj->info)
+#define sc_dump(obj, fp) \
+ obj->dump(obj->info, fp)
+
+/*
+ * Do not call the below functions from loadable modules. Doing so
+ * requires linking the configuration library in to the modules, which
+ * is what we want to avoid.
+ */
+
+/* Returns a copy of our simple config object */
+config_object_t *sc_init(void);
+
+/* Frees a previously-allocated copy of our simple config object */
+void sc_release(config_object_t *c);
+
+int check_file_permissions(const char *fname);
+
+int do_configure(config_object_t *config, const char *filename);
+
+#endif
diff --git a/agents/virt/include/static_map.h b/agents/virt/include/static_map.h
new file mode 100644
index 0000000..736e823
--- /dev/null
+++ b/agents/virt/include/static_map.h
@@ -0,0 +1,34 @@
+#ifndef _STATIC_MAP_H
+#define _STATIC_MAP_H
+
+typedef int (*map_load_t)(void *config, void **perm_info);
+typedef int (*map_check_t)(void *info, const char *src, const char *tgt_uuid, const char *tgt_name);
+typedef void (*map_cleanup_t)(void **info);
+
+typedef struct {
+ map_load_t load;
+ map_check_t check;
+ map_cleanup_t cleanup;
+ void *info;
+} map_object_t;
+
+/*
+ * These macros may be called from within a loadable module
+ */
+#define map_load(obj, config) \
+ obj->load(config, &obj->info)
+#define map_check(obj, src, tgt_uuid) \
+ obj->check(obj->info, src, tgt_uuid, NULL)
+#define map_check2(obj, src, tgt_uuid, tgt_name) \
+ obj->check(obj->info, src, tgt_uuid, tgt_name)
+#define map_free(obj) \
+ obj->cleanup(obj->info)
+
+/* Returns a copy of our simple config object */
+void *map_init(void);
+
+/* Frees a previously-allocated copy of our simple config object */
+void map_release(void *c);
+
+
+#endif
diff --git a/agents/virt/include/tcp.h b/agents/virt/include/tcp.h
new file mode 100644
index 0000000..609f3e9
--- /dev/null
+++ b/agents/virt/include/tcp.h
@@ -0,0 +1,27 @@
+/*
+ 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.
+*/
+#ifndef _XVM_TCP_H
+#define _XVM_TCP_H
+
+int ipv4_connect(struct in_addr *in_addr, uint16_t port, int timeout);
+int ipv6_connect(struct in6_addr *in6_addr, uint16_t port, int timeout);
+int ipv4_listen(const char *addr_str, uint16_t port, int backlog);
+int ipv6_listen(const char *addr_str, uint16_t port, int backlog);
+
+#endif
diff --git a/agents/virt/include/tcp_listener.h b/agents/virt/include/tcp_listener.h
new file mode 100644
index 0000000..75dec93
--- /dev/null
+++ b/agents/virt/include/tcp_listener.h
@@ -0,0 +1,7 @@
+#ifndef __TCP_LISTENER_H
+#define __TCP_LISTENER_H
+
+#define IPV4_TCP_ADDR_DEFAULT "127.0.0.1"
+#define IPV6_TCP_ADDR_DEFAULT "::1"
+
+#endif
diff --git a/agents/virt/include/xvm.h b/agents/virt/include/xvm.h
new file mode 100644
index 0000000..bcd7db2
--- /dev/null
+++ b/agents/virt/include/xvm.h
@@ -0,0 +1,158 @@
+/*
+ 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.
+*/
+#ifndef _XVM_H
+#define _XVM_H
+
+#include <stdint.h>
+#include <sechash.h>
+#include <netinet/in.h>
+#include <byteswap.h>
+#include <endian.h>
+
+#define XVM_VERSION "1.9.0"
+
+#define MAX_DOMAINNAME_LENGTH 64 /* XXX MAXHOSTNAMELEN */
+#define MAX_ADDR_LEN sizeof(struct sockaddr_in6)
+#define DOMAIN0NAME "Domain-0"
+#define DOMAIN0UUID "00000000-0000-0000-0000-000000000000"
+
+typedef enum {
+ HASH_NONE = 0x0, /* No packet signing */
+ HASH_SHA1 = 0x1, /* SHA1 signing */
+ HASH_SHA256 = 0x2, /* SHA256 signing */
+ HASH_SHA512 = 0x3 /* SHA512 signing */
+} fence_hash_t;
+
+#define DEFAULT_HASH HASH_SHA256
+
+typedef enum {
+ AUTH_NONE = 0x0, /* Plain TCP */
+ AUTH_SHA1 = 0x1, /* Challenge-response (SHA1) */
+ AUTH_SHA256 = 0x2, /* Challenge-response (SHA256) */
+ AUTH_SHA512 = 0x3 /* Challenge-response (SHA512) */
+ /* AUTH_SSL_X509 = 0x10 SSL X509 certificates */
+} fence_auth_type_t;
+
+#define DEFAULT_AUTH AUTH_SHA256
+
+typedef enum {
+ FENCE_NULL = 0x0,
+ FENCE_OFF = 0x1, /* Turn the VM off */
+ FENCE_REBOOT = 0x2, /* Hit the reset button */
+ FENCE_ON = 0x3, /* Turn the VM on */
+ FENCE_STATUS = 0x4, /* virtual machine status (off/on) */
+ FENCE_DEVSTATUS = 0x5, /* Status of the fencing device */
+ FENCE_HOSTLIST = 0x6, /* List VMs controllable */
+ FENCE_METADATA = 0x7, /* Print fence agent metadata */
+ FENCE_VALIDATEALL = 0x8 /* Validate command-line or stdin arguments and exit */
+} fence_cmd_t;
+
+#define DEFAULT_TTL 4
+
+#ifndef DEFAULT_HYPERVISOR_URI
+#define DEFAULT_HYPERVISOR_URI "qemu:///system"
+#endif
+
+#define MAX_HASH_LENGTH SHA512_LENGTH
+#define MAX_KEY_LEN 4096
+
+typedef struct __attribute__ ((packed)) _fence_req {
+ uint8_t request; /* Fence request */
+ uint8_t hashtype; /* Hash type used */
+ uint8_t addrlen; /* Length of address */
+ uint8_t flags; /* Special flags */
+#define RF_UUID 0x1 /* Flag specifying UUID */
+ uint8_t domain[MAX_DOMAINNAME_LENGTH]; /* Domain to fence*/
+ uint8_t address[MAX_ADDR_LEN]; /* We're this IP */
+#define DEFAULT_MCAST_PORT 1229
+ uint16_t port; /* Port we bound to */
+ uint8_t random[6]; /* Random Data */
+ uint32_t seqno; /* Request identifier; can be random */
+ uint32_t family; /* Address family */
+ uint8_t hash[MAX_HASH_LENGTH]; /* Binary hash */
+} fence_req_t;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define swab_fence_req_t(req) \
+do { \
+ (req)->seqno = bswap_32((req)->seqno); \
+ (req)->family = bswap_32((req)->family); \
+ (req)->port = bswap_32((req)->port); \
+} while(0)
+#else
+#define swab_fence_req_t(req)
+#endif
+
+
+/* for host list */
+typedef struct __attribute__ ((packed)) _host_info {
+ uint8_t domain[MAX_DOMAINNAME_LENGTH];
+ uint8_t uuid[MAX_DOMAINNAME_LENGTH];
+ uint8_t state;
+ uint8_t pad;
+} host_state_t;
+
+
+#define DEFAULT_SERIAL_DEVICE "/dev/ttyS1"
+#define DEFAULT_SERIAL_SPEED "115200,8N1"
+#define DEFAULT_CHANNEL_IP "10.0.2.179"
+#define SERIAL_MAGIC 0x61626261 /* endian doesn't matter */
+
+typedef struct __attribute__((packed)) _serial_fence_req {
+ uint32_t magic;
+ uint8_t request;
+ uint8_t flags;
+ uint8_t domain[MAX_DOMAINNAME_LENGTH];
+ uint32_t seqno;
+} serial_req_t;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define swab_serial_req_t(req) \
+do { \
+ (req)->magic = bswap_32((req)->magic); \
+ (req)->seqno = bswap_32((req)->seqno); \
+} while(0)
+#else
+#define swab_serial_req_t(req)
+#endif
+
+
+typedef struct __attribute__((packed)) _serial_fense_resp {
+ uint32_t magic;
+ uint8_t response;
+} serial_resp_t;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define swab_serial_resp_t(req) \
+do { \
+ (req)->magic = bswap_32((req)->magic); \
+} while(0)
+#else
+#define swab_serial_resp_t(req)
+#endif
+
+
+#define RESP_SUCCESS 0
+#define RESP_FAIL 1
+#define RESP_OFF 2
+#define RESP_PERM 3
+#define RESP_HOSTLIST 253
+
+
+#endif
diff --git a/agents/virt/man/.gitignore b/agents/virt/man/.gitignore
new file mode 100644
index 0000000..4a80f6e
--- /dev/null
+++ b/agents/virt/man/.gitignore
@@ -0,0 +1,2 @@
+fence_virt.8
+fence_virt.wiki
diff --git a/agents/virt/man/Makefile.am b/agents/virt/man/Makefile.am
new file mode 100644
index 0000000..5b8ae36
--- /dev/null
+++ b/agents/virt/man/Makefile.am
@@ -0,0 +1,28 @@
+###############################################################################
+###############################################################################
+##
+## Copyright (C) 2009-2021 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
+
+dist_man8_MANS = fence_xvm.8 fence_virtd.8
+dist_man5_MANS = fence_virt.conf.5
+man8_MANS = fence_virt.8
+
+fence_virt.8: $(top_builddir)/agents/virt/client/fence_virt $(top_srcdir)/lib/fence2man.xsl
+ set -e && \
+ ../client/$(@:%.8=%) -o metadata > .$(@F).tmp && \
+ xmllint --noout --relaxng $(top_srcdir)/lib/metadata.rng .$(@F).tmp && \
+ xsltproc $(top_srcdir)/lib/fence2man.xsl .$(@F).tmp > $@
+ xsltproc $(top_srcdir)/lib/fence2wiki.xsl .$(@F).tmp | grep -v '<?xml' > $(@F:%.8=%.wiki)
+
+
+clean-local:
+ rm -f fence_virt.8* .*.8.tmp *.wiki
diff --git a/agents/virt/man/fence_virt.conf.5 b/agents/virt/man/fence_virt.conf.5
new file mode 100644
index 0000000..f920a66
--- /dev/null
+++ b/agents/virt/man/fence_virt.conf.5
@@ -0,0 +1,335 @@
+.TH fence_virt.conf 5
+
+.SH NAME
+fence_virt.conf - configuration file for fence_virtd
+
+.SH DESCRIPTION
+
+The fence_virt.conf file contains configuration information for fence_virtd,
+a fencing request routing daemon for clusters of virtual machines.
+
+The file is tree-structured. There are parent/child relationships and sibling
+relationships between the nodes.
+
+ foo {
+ bar {
+ baz = "1";
+ }
+ }
+
+There are three primary sections of fence_virt.conf.
+
+.SH SECTIONS
+.SS fence_virtd
+
+This section contains global information about how fence_virtd is to operate.
+The most important pieces of information are as follows:
+
+.TP
+.B listener
+.
+the listener plugin for receiving fencing requests from clients
+
+.TP
+.B backend
+.
+the plugin to be used to carry out fencing requests
+
+.TP
+.B foreground
+.
+do not fork into the background.
+
+.TP
+.B wait_for_init
+.
+wait for the frontend and backends to become available rather than giving up immediately.
+This replaces wait_for_backend in 0.2.x.
+
+.TP
+.B module_path
+.
+the module path to search for plugins
+
+.SS listeners
+
+This section contains listener-specific configuration information; see the
+section about listeners below.
+
+.SS backends
+
+This section contains listener-specific configuration information; see the
+section about listeners below.
+
+.SS groups
+
+This section contains static maps of which virtual machines
+may fence which other virtual machines; see the section
+about groups below.
+
+
+.SH LISTENERS
+
+There are various listeners available for fence_virtd, each one handles
+decoding and authentication of a given fencing request. The following
+configuration blocks belong in the \fBlisteners\fP section of fence_virt.conf
+
+.SS multicast
+.TP
+.B key_file
+.
+the shared key file to use (default: /etc/cluster/fence_xvm.key).
+
+.TP
+.B hash
+.
+the weakest hashing algorithm allowed for client requests. Clients may send packets with stronger hashes than the one specified, but not weaker ones. (default: sha256, but could
+be sha1, sha512, or none)
+
+.TP
+.B auth
+.
+the hashing algorithm to use for the simplistic challenge-response authentication
+(default: sha256, but could be sha1, sha512, or none)
+
+.TP
+.B family
+.
+the IP family to use (default: ipv4, but may be ipv6)
+
+.TP
+.B address
+.
+the multicast address to listen on (default: 225.0.0.12)
+
+.TP
+.B port
+.
+the multicast port to listen on (default: 1229)
+
+.TP
+.B interface
+.
+interface to listen on. By default, fence_virtd listens on all interfaces.
+However, this causes problems in some environments where the host computer
+is used as a gateway.
+
+.SS serial
+
+The serial listener plugin utilizes libvirt's serial (or VMChannel)
+mapping to listen for requests. When using the serial listener, it is
+necessary to add a serial port (preferably pointing to /dev/ttyS1) or
+a channel (preferably pointing to 10.0.2.179:1229) to the
+libvirt domain description. Note that only type
+.B unix
+, mode
+.B bind
+serial ports and channels are supported and each VM should have a
+separate unique socket. Example libvirt XML:
+
+.in 8
+ <\fBserial\fP type='unix'>
+ <source mode='bind' path='/sandbox/guests/fence_socket_molly'/>
+ <target port='1'/>
+ </serial>
+ <\fBchannel\fP type='unix'>
+ <source mode='bind' path='/sandbox/guests/fence_molly_vmchannel'/>
+ <target type='guestfwd' address='10.0.2.179' port='1229'/>
+ </channel>
+.in 0
+
+.TP
+.B uri
+.
+the URI to use when connecting to libvirt by the serial plugin (optional).
+
+.TP
+.B path
+.
+The same directory that is defined for the domain serial port path (From example above: /sandbox/guests). Sockets must reside in this directory in order to be considered valid. This can be used to prevent fence_virtd from using the wrong sockets.
+
+.TP
+.B mode
+.
+This selects the type of sockets to register. Valid values are "serial"
+(default) and "vmchannel".
+
+.SS tcp
+The tcp listener operates similarly to the multicast listener but uses TCP sockets for communication instead of using multicast packets.
+
+.TP
+.B key_file
+.
+the shared key file to use (default: /etc/cluster/fence_xvm.key).
+
+.TP
+.B hash
+.
+the hashing algorithm to use for packet signing (default: sha256, but could
+be sha1, sha512, or none)
+
+.TP
+.B auth
+.
+the hashing algorithm to use for the simplistic challenge-response authentication
+(default: sha256, but could be sha1, sha512, or none)
+
+.TP
+.B family
+.
+the IP family to use (default: ipv4, but may be ipv6)
+
+.TP
+.B address
+.
+the IP address to listen on (default: 127.0.0.1 for IPv4, ::1 for IPv6)
+
+.TP
+.B port
+.
+the TCP port to listen on (default: 1229)
+
+.SS vsock
+The vsock listener operates similarly to the multicast listener but uses virtual machine sockets (AF_VSOCK) for communication instead of using multicast packets.
+
+.TP
+.B key_file
+.
+the shared key file to use (default: /etc/cluster/fence_xvm.key).
+
+.TP
+.B hash
+.
+the hashing algorithm to use for packet signing (default: sha256, but could
+be sha1, sha512, or none)
+
+.TP
+.B auth
+.
+the hashing algorithm to use for the simplistic challenge-response authentication
+(default: sha256, but could be sha1, sha512, or none)
+
+.TP
+.B port
+.
+the vsock port to listen on (default: 1229)
+
+.SH BACKENDS
+
+There are various backends available for fence_virtd, each one handles
+routing a fencing request to a hypervisor or management tool. The following
+configuration blocks belong in the \fBbackends\fP section of fence_virt.conf
+
+.SS libvirt
+
+The libvirt plugin is the simplest plugin. It is used in environments where
+routing fencing requests between multiple hosts is not required, for example
+by a user running a cluster of virtual machines on a single desktop computer.
+
+.TP
+.B uri
+.
+the URI to use when connecting to libvirt.
+
+All libvirt URIs are accepted and passed as-is.
+
+See https://libvirt.org/uri.html#remote-uris for examples.
+
+NOTE: When VMs are run as non-root user the socket path must be set as part
+of the URI.
+
+Example: qemu:///session?socket=/run/user/<UID>/libvirt/virtqemud-sock
+
+.SS cpg
+
+The cpg plugin uses corosync CPG and libvirt to track virtual
+machines and route fencing requests to the appropriate computer.
+
+.TP
+.B uri
+.
+the URI to use when connecting to libvirt by the cpg plugin.
+
+.TP
+.B name_mode
+.
+The cpg plugin, in order to retain compatibility with fence_xvm,
+stores virtual machines in a certain way. The
+default was to use 'name' when using fence_xvm and fence_xvmd, and so this
+is still the default. However, it is strongly recommended to use 'uuid'
+instead of 'name' in all cluster environments involving more than one
+physical host in order to avoid the potential for name collisions.
+
+.SH GROUPS
+
+Fence_virtd supports static maps which allow grouping of VMs. The
+groups are arbitrary and are checked at fence time. Any member of
+a group may fence any other member. Hosts may be assigned to multiple
+groups if desired.
+
+.SS group
+
+This defines a group.
+
+.TP
+.B name
+.
+Optionally define the name of the group. Useful only for configuration
+readability and debugging of configuration parsing.
+
+.TP
+.B uuid
+.
+Defines UUID as a member of a group. It can be used multiple times
+to specify both node name and UUID values that can be fenced.
+When using the serial listener, the vm uuid is required and it is
+recommended to add also the vm name.
+
+.TP
+.B ip
+.
+Defines an IP which is allowed to send fencing requests
+for members of this group (e.g. for multicast). It can be used
+multiple times to allow more than 1 IP to send fencing requests to
+the group. It is highly recommended that this be used in conjunction
+with a key file.
+When using the vsock listener, ip should contain the CID value assigned
+by libvirt to the vm.
+When using the serial listener, ip value is not used and can be omitted.
+
+
+.SH EXAMPLE
+
+ fence_virtd {
+ listener = "multicast";
+ backend = "cpg";
+ }
+
+ # this is the listeners section
+
+ listeners {
+ multicast {
+ key_file = "/etc/cluster/fence_xvm.key";
+ }
+ }
+
+ backends {
+ libvirt {
+ uri = "qemu:///system";
+ }
+ }
+
+ groups {
+ group {
+ name = "cluster1";
+ ip = "192.168.1.1";
+ ip = "192.168.1.2";
+ uuid = "44179d3f-6c63-474f-a212-20c8b4b25b16";
+ uuid = "1ce02c4b-dfa1-42cb-b5b1-f0b1091ece60";
+ uuid = "node1";
+ uuid = "node2";
+ }
+ }
+
+.SH SEE ALSO
+fence_virtd(8), fence_virt(8), fence_xvm(8), fence(8)
diff --git a/agents/virt/man/fence_virtd.8 b/agents/virt/man/fence_virtd.8
new file mode 100644
index 0000000..56f6938
--- /dev/null
+++ b/agents/virt/man/fence_virtd.8
@@ -0,0 +1,53 @@
+.TH FENCE_AGENT 8 2010-01-05 "fence_virtd (Fence Agent)"
+.SH NAME
+fence_virtd - Fencing host for virtual machines
+
+.SH DESCRIPTION
+.P
+fence_virtd is a host daemon designed to route fencing requests for
+virtual machines.
+
+Fence_virt and fence_xvm talk to fence_virtd, which supports multiple backend plugins, including:
+ - libvirt for single-node operation
+ - Corosync CPG and libvirt when using Linux-cluster release 3.0.0 or later
+ - libvirt-qpid for multi-node, non-cluster operation
+
+For compatibility, fence_xvm from linux-cluster release 2 may talk to fence_virtd.
+
+.P
+fence_virtd accepts a few options on the command line, but most options
+are read from fence_virt.conf.
+
+.SH PARAMETERS
+.TP
+.B -d
+.
+Specify debug level, e.g. "-d99"
+
+.TP
+.B -c
+.
+Interactively prompt user for configuration information
+
+.TP
+.B -f
+.
+Specify an alternate configuration file instead of /etc/fence_virt.conf
+
+.TP
+.B -F
+.
+Do not fork into background after starting (overrides any setting in fence_virt.conf)
+
+.TP
+.B -w
+.
+Wait for backend to be available (overrides any setting in fence_virt.conf)
+
+.TP
+.B -p
+.
+Specify the full path to the pid file used to record the active process pid.
+
+.SH SEE ALSO
+fence_virt(8), fence_xvm(8), fence(8), fence_virt.conf(5)
diff --git a/agents/virt/man/fence_xvm.8 b/agents/virt/man/fence_xvm.8
new file mode 100644
index 0000000..5d0cfdd
--- /dev/null
+++ b/agents/virt/man/fence_xvm.8
@@ -0,0 +1 @@
+.so man8/fence_virt.8
diff --git a/agents/virt/server/Makefile.am b/agents/virt/server/Makefile.am
new file mode 100644
index 0000000..fbca617
--- /dev/null
+++ b/agents/virt/server/Makefile.am
@@ -0,0 +1,79 @@
+###############################################################################
+###############################################################################
+##
+## Copyright (C) 2009-2021 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
+
+noinst_HEADERS = cpg.h serial.h uuid-test.h virt.h
+
+sbin_PROGRAMS = fence_virtd
+
+#
+# daemon
+#
+fence_virtd_SOURCES = main.c plugin.c config.c static_map.c uuid-test.c \
+ daemon_init.c
+
+fence_virtd_CFLAGS = $(VIRT_AM_CFLAGS) \
+ $(nss_CFLAGS) $(xml2_CFLAGS) $(uuid_CFLAGS) $(PTHREAD_CFLAGS) \
+ $(AM_CFLAGS)
+
+fence_virtd_LDADD = $(VIRT_CONFIG_LIBS) $(VIRT_COMMON_LIBS) \
+ $(nss_LIBS) $(xml2_LIBS) $(uuid_LIBS) $(PTHREAD_LIBS) $(dl_LIBS)
+
+fence_virtd_LDFLAGS = $(VIRT_AM_LDFLAGS) $(VIRT_COMMON_LDFLAGS)
+
+virt_la_SOURCES = libvirt.c virt.c uuid-test.c
+cpg_la_SOURCES = cpg-virt.c cpg.c virt.c uuid-test.c
+multicast_la_SOURCES = mcast.c history.c
+tcp_la_SOURCES = tcp.c history.c
+vsock_la_SOURCES = vsock.c history.c
+serial_la_SOURCES = virt-serial.c virt-sockets.c serial.c history.c
+
+fence_virtd_CFLAGS += -DMODULE_PATH=\"$(libdir)/fence-virt/\"
+
+fvlibdir = $(libdir)/fence-virt
+
+fvlib_LTLIBRARIES =
+
+MODULESCFLAGS = $(VIRT_AM_CFLAGS) $(AM_CFLAGS)
+MODULESLDFLAGS = $(VIRT_AM_LDFLAGS) $(VIRT_COMMON_LIBS) $(VIRT_COMMON_LDFLAGS) -module -avoid-version -export-dynamic
+
+if modlibvirt
+fvlib_LTLIBRARIES += virt.la
+virt_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(virt_CFLAGS)
+virt_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(virt_LIBS)
+endif
+if modcpg
+fvlib_LTLIBRARIES += cpg.la
+cpg_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(cpg_CFLAGS) $(virt_CFLAGS)
+cpg_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(cpg_LIBS) $(virt_LIBS)
+endif
+if modmulticast
+fvlib_LTLIBRARIES += multicast.la
+multicast_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS)
+multicast_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS)
+endif
+if modserial
+fvlib_LTLIBRARIES += serial.la
+serial_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(xml2_CFLAGS) $(virt_CFLAGS)
+serial_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(xml2_LIBS) $(virt_LIBS)
+endif
+if modtcp
+fvlib_LTLIBRARIES += tcp.la
+tcp_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS)
+tcp_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS)
+endif
+if modvsock
+fvlib_LTLIBRARIES += vsock.la
+vsock_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS)
+vsock_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS)
+endif
diff --git a/agents/virt/server/config.c b/agents/virt/server/config.c
new file mode 100644
index 0000000..fa9af97
--- /dev/null
+++ b/agents/virt/server/config.c
@@ -0,0 +1,698 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "mcast.h"
+#include "xvm.h"
+#include "server_plugin.h"
+#include "simple_auth.h"
+
+
+static int
+yesno(const char *prompt, int dfl)
+{
+ char result[10];
+
+ printf("%s [%c/%c]? ", prompt, dfl?'Y':'y', dfl?'n':'N');
+ fflush(stdout);
+
+ memset(result, 0, sizeof(result));
+ if (fgets(result, 9, stdin) == NULL)
+ return dfl;
+
+ if (result[0] == 'y' || result[0] == 'Y')
+ return 1;
+ if (result[0] == 'n' || result[0] == 'N')
+ return 0;
+
+ return dfl;
+}
+
+
+static int
+text_input(const char *prompt, const char *dfl, char *input, size_t len)
+{
+ const char *tmpdfl = dfl;
+ const char *nulldfl = "";
+
+ if (dfl == NULL) {
+ tmpdfl = nulldfl;
+ }
+
+ printf("%s [%s]: ", prompt, tmpdfl);
+ fflush(stdout);
+
+ memset(input, 0, len);
+ if (fgets(input, len, stdin) == NULL) {
+ strncpy(input, tmpdfl, len);
+ return 0;
+ }
+ if (input[strlen(input)-1] == '\n')
+ input[strlen(input)-1] = 0;
+
+ if (strlen(input) == 0) {
+ strncpy(input, tmpdfl, len);
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int
+plugin_path_configure(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done = 0;
+
+ if (sc_get(config, "fence_virtd/@module_path", val,
+ sizeof(val))) {
+#ifdef MODULE_PATH
+ snprintf(val, sizeof(val), MODULE_PATH);
+#else
+ printf("Failed to determine module search path.\n");
+#endif
+ }
+
+ do {
+ text_input("Module search path", val, inp, sizeof(inp));
+
+ printf("\n");
+ done = plugin_search(inp);
+ if (done > 0) {
+ plugin_dump();
+ done = 1;
+ } else {
+ done = 0;
+ printf("No modules found in %s!\n", inp);
+ if (yesno("Use this value anyway", 0) == 1)
+ done = 1;
+ }
+ } while (!done);
+
+ sc_set(config, "fence_virtd/@module_path", inp);
+
+ return 0;
+}
+
+
+static int
+backend_config_libvirt(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+
+ printf("\n");
+ printf("The libvirt backend module is designed for single desktops or\n"
+ "servers. Do not use in environments where virtual machines\n"
+ "may be migrated between hosts.\n\n");
+
+ /* Default backend plugin */
+ if (sc_get(config, "backends/libvirt/@uri", val,
+ sizeof(val))) {
+ strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
+ }
+
+ text_input("Libvirt URI", val, inp, sizeof(inp));
+
+ sc_set(config, "backends/libvirt/@uri", inp);
+
+ return 0;
+}
+
+
+static int
+backend_config_cpg(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done = 0;
+
+ printf("\n");
+ printf("The CPG backend module is designed for use in clusters\n"
+ "running corosync and libvirt. It utilizes the CPG API to \n"
+ "route fencing requests, finally utilizing libvirt to perform\n"
+ "fencing actions.\n\n");
+
+ if (sc_get(config, "backends/cpg/@uri", val,
+ sizeof(val))) {
+ strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
+ }
+
+ text_input("Libvirt URI", val, inp, sizeof(inp));
+
+ sc_set(config, "backends/cpg/@uri", inp);
+
+ printf("\n");
+ printf("The name mode is how the cpg plugin stores and \n"
+ "references virtual machines. Since virtual machine names\n"
+ "are not guaranteed to be unique cluster-wide, use of UUIDs\n"
+ "is strongly recommended. However, for compatibility with \n"
+ "fence_xvmd, the use of 'name' mode is also supported.\n\n");
+
+ if (sc_get(config, "backends/cpg/@name_mode", val,
+ sizeof(val))) {
+ strncpy(val, "uuid", sizeof(val));
+ }
+
+ do {
+ text_input("VM naming/tracking mode (name or uuid)",
+ val, inp, sizeof(inp));
+ if (!strcasecmp(inp, "uuid")) {
+ done = 1;
+ } else if (!strcasecmp(inp, "name")) {
+ done = 0;
+ printf("This can be dangerous if you do not take care to"
+ "ensure that\n"
+ "virtual machine names are unique "
+ "cluster-wide.\n");
+ if (yesno("Use name mode anyway", 1) == 1)
+ done = 1;
+ }
+ } while (!done);
+
+ sc_set(config, "backends/cpg/@name_mode", inp);
+
+ return 0;
+}
+
+
+static int
+listener_config_multicast(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ const char *family = "ipv4";
+ struct in_addr sin;
+ struct in6_addr sin6;
+ int done = 0;
+
+ printf("\n");
+ printf("The multicast listener module is designed for use environments\n"
+ "where the guests and hosts may communicate over a network using\n"
+ "multicast.\n\n");
+
+
+ /* MULTICAST IP ADDRESS/FAMILY */
+ printf("The multicast address is the address that a client will use to\n"
+ "send fencing requests to fence_virtd.\n\n");
+
+ if (sc_get(config, "listeners/multicast/@address",
+ val, sizeof(val)-1)) {
+ strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Multicast IP Address", val, inp, sizeof(inp));
+
+ if (inet_pton(AF_INET, inp, &sin) == 1) {
+ printf("\nUsing ipv4 as family.\n\n");
+ family = "ipv4";
+ done = 1;
+ } else if (inet_pton(AF_INET6, inp, &sin6) == 1) {
+ printf("\nUsing ipv6 as family.\n\n");
+ family = "ipv6";
+ done = 1;
+ } else
+ printf("'%s' is not a valid IP address!\n", inp);
+ } while (!done);
+
+ sc_set(config, "listeners/multicast/@family", family);
+ sc_set(config, "listeners/multicast/@address", inp);
+
+ /* MULTICAST IP PORT */
+ if (sc_get(config, "listeners/multicast/@port",
+ val, sizeof(val)-1)) {
+ snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT);
+ }
+
+ done = 0;
+ do {
+ char *p;
+ int ret;
+
+ text_input("Multicast IP Port", val, inp, sizeof(inp));
+ ret = strtol(inp, &p, 0);
+ if (*p != '\0' || ret <= 0 || ret >= 65536) {
+ printf("Port value '%s' is out of range\n", val);
+ continue;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "listeners/multicast/@port", inp);
+
+ /* MULTICAST INTERFACE */
+ printf("\nSetting a preferred interface causes fence_virtd to listen only\n"
+ "on that interface. Normally, it listens on all interfaces.\n"
+ "In environments where the virtual machines are using the host\n"
+ "machine as a gateway, this *must* be set (typically to virbr0).\n"
+ "Set to 'none' for no interface.\n\n"
+ );
+
+ if (sc_get(config, "listeners/multicast/@interface",
+ val, sizeof(val)-1)) {
+ strncpy(val, "none", sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Interface", val, inp, sizeof(inp));
+
+ if (!strcasecmp(inp, "none")) {
+ break;
+ }
+
+ if (strlen(inp) > 0) {
+ int ret;
+
+ ret = if_nametoindex(inp);
+ if (ret < 0) {
+ printf("Invalid interface: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } else
+ printf("No interface given\n");
+ } while (!done);
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/multicast/@interface", NULL);
+ } else {
+ sc_set(config, "listeners/multicast/@interface", inp);
+ }
+
+
+ /* KEY FILE */
+ printf("\nThe key file is the shared key information which is used to\n"
+ "authenticate fencing requests. The contents of this file must\n"
+ "be distributed to each physical host and virtual machine within\n"
+ "a cluster.\n\n");
+
+ if (sc_get(config, "listeners/multicast/@key_file",
+ val, sizeof(val)-1)) {
+ strncpy(val, DEFAULT_KEY_FILE, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Key File", val, inp, sizeof(inp));
+
+ if (!strcasecmp(inp, "none")) {
+ break;
+ }
+
+ if (strlen(inp) > 0) {
+ if (inp[0] != '/') {
+ printf("Invalid key file: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } else
+ printf("No key file given\n");
+ } while (!done);
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/multicast/@key_file", NULL);
+ } else {
+ sc_set(config, "listeners/multicast/@key_file", inp);
+ }
+
+ return 0;
+}
+
+static int
+listener_config_tcp(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ const char *family = "ipv4";
+ struct in_addr sin;
+ struct in6_addr sin6;
+ int done = 0;
+
+ printf("\n");
+ printf("The TCP listener module is designed for use in environments\n"
+ "where the guests and hosts communicate over viosproxy.\n\n");
+
+ /* IP ADDRESS/FAMILY */
+ printf("The IP address is the address that a client will use to\n"
+ "send fencing requests to fence_virtd.\n\n");
+
+ if (sc_get(config, "listeners/tcp/@address",
+ val, sizeof(val)-1)) {
+ strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("TCP Listen IP Address", val, inp, sizeof(inp));
+
+ if (inet_pton(AF_INET, inp, &sin) == 1) {
+ printf("\nUsing ipv4 as family.\n\n");
+ family = "ipv4";
+ done = 1;
+ } else if (inet_pton(AF_INET6, inp, &sin6) == 1) {
+ printf("\nUsing ipv6 as family.\n\n");
+ family = "ipv6";
+ done = 1;
+ } else {
+ printf("'%s' is not a valid IP address!\n", inp);
+ continue;
+ }
+ } while (!done);
+
+ sc_set(config, "listeners/tcp/@family", family);
+ sc_set(config, "listeners/tcp/@address", inp);
+
+ /* MULTICAST IP PORT */
+ if (sc_get(config, "listeners/tcp/@port",
+ val, sizeof(val)-1)) {
+ snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT);
+ }
+
+ done = 0;
+ do {
+ char *p;
+ int ret;
+
+ text_input("TCP Listen Port", val, inp, sizeof(inp));
+
+ ret = strtol(inp, &p, 0);
+ if (*p != '\0' || ret <= 0 || ret >= 65536) {
+ printf("Port value '%s' is out of range\n", val);
+ continue;
+ }
+ done = 1;
+ } while (!done);
+ sc_set(config, "listeners/tcp/@port", inp);
+
+ /* KEY FILE */
+ printf("\nThe key file is the shared key information which is used to\n"
+ "authenticate fencing requests. The contents of this file must\n"
+ "be distributed to each physical host and virtual machine within\n"
+ "a cluster.\n\n");
+
+ if (sc_get(config, "listeners/tcp/@key_file",
+ val, sizeof(val)-1)) {
+ strncpy(val, DEFAULT_KEY_FILE, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Key File", val, inp, sizeof(inp));
+
+ if (!strcasecmp(inp, "none")) {
+ break;
+ }
+
+ if (strlen(inp) > 0) {
+ if (inp[0] != '/') {
+ printf("Invalid key file: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } else
+ printf("No key file given\n");
+ } while (!done);
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/tcp/@key_file", NULL);
+ } else {
+ sc_set(config, "listeners/tcp/@key_file", inp);
+ }
+
+ return 0;
+}
+
+static int
+listener_config_serial(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done;
+
+ printf("\n");
+ printf("The serial plugin allows fence_virtd to communicate with\n"
+ "guests using serial or guest-forwarding VMChannel instead\n"
+ "of using TCP/IP networking.\n\n");
+ printf("Special configuration of virtual machines is required. See\n"
+ "fence_virt.conf(5) for more details.\n\n");
+
+ if (sc_get(config, "listeners/serial/@uri",
+ val, sizeof(val)-1)) {
+ strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
+ }
+
+ text_input("Libvirt URI", val, inp, sizeof(inp));
+
+ printf("\nSetting a socket path prevents fence_virtd from taking\n"
+ "hold of all Unix domain sockets created when the guest\n"
+ "is started. A value like /var/run/cluster/fence might\n"
+ "be a good value. Don't forget to create the directory!\n\n");
+
+ if (sc_get(config, "listeners/serial/@path",
+ val, sizeof(val)-1)) {
+ strncpy(val, "none", sizeof(val));
+ }
+
+ text_input("Socket directory", val, inp, sizeof(inp));
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/serial/@path", NULL);
+ } else {
+ sc_set(config, "listeners/serial/@path", inp);
+ }
+
+ printf("\nThe serial plugin allows two types of guest to host\n"
+ "configurations. One is via a serial port; the other is\n"
+ "utilizing the newer VMChannel.\n\n");
+
+ if (sc_get(config, "listeners/serial/@mode",
+ val, sizeof(val)-1)) {
+ strncpy(val, "serial", sizeof(val));
+ }
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/serial/@path", NULL);
+ } else {
+ sc_set(config, "listeners/serial/@path", inp);
+ }
+
+ done = 0;
+ do {
+ text_input("Mode (serial or vmchannel)", val, inp,
+ sizeof(inp));
+
+ if (strcasecmp(inp, "serial") && strcasecmp(inp, "vmchannel")) {
+ printf("Invalid mode: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "listeners/serial/@mode", inp);
+ return 0;
+}
+
+
+static int
+backend_configure(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done;
+
+ printf("\n");
+ printf("Backend modules are responsible for routing requests to\n"
+ "the appropriate hypervisor or management layer.\n\n");
+
+ /* Default backend plugin */
+ if (sc_get(config, "fence_virtd/@backend", val,
+ sizeof(val))) {
+ strncpy(val, "libvirt", sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Backend module", val, inp, sizeof(inp));
+ if (plugin_find_backend(inp) == NULL) {
+ printf("No backend module named %s found!\n", inp);
+ if (yesno("Use this value anyway", 0) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "fence_virtd/@backend", inp);
+
+ if (!strcmp(inp, "libvirt")) {
+ backend_config_libvirt(config);
+ } else if (!strcmp(inp, "cpg")) {
+ backend_config_cpg(config);
+ }
+
+ return 0;
+}
+
+
+static int
+listener_configure(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done;
+
+ printf("\n");
+ printf("Listener modules are responsible for accepting requests\n"
+ "from fencing clients.\n\n");
+
+ /* Default backend plugin */
+ if (sc_get(config, "fence_virtd/@listener", val,
+ sizeof(val))) {
+ strncpy(val, "multicast", sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Listener module", val, inp, sizeof(inp));
+ if (plugin_find_listener(inp) == NULL) {
+ printf("No listener module named %s found!\n", inp);
+ if (yesno("Use this value anyway", 0) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "fence_virtd/@listener", inp);
+ if (!strcmp(inp, "multicast"))
+ listener_config_multicast(config);
+ else if (!strcmp(inp, "tcp"))
+ listener_config_tcp(config);
+ else if (!strcmp(inp, "serial"))
+ listener_config_serial(config);
+ else
+ printf("Unable to configure unknown listner module '%s'\n", inp);
+
+ return 0;
+}
+
+
+int
+check_file_permissions(const char *fname)
+{
+ struct stat st;
+ mode_t file_perms = 0600;
+ int ret;
+
+ ret = stat(fname, &st);
+ if (ret != 0) {
+ printf("stat failed on file '%s': %s\n",
+ fname, strerror(errno));
+ return 1;
+ }
+
+ if ((st.st_mode & 0777) != file_perms) {
+ printf("Insecure permissions on file "
+ "'%s': changing from 0%o to 0%o.\n", fname,
+ (unsigned int)(st.st_mode & 0777),
+ (unsigned int)file_perms);
+ if (chmod(fname, file_perms) != 0) {
+ printf("Unable to change permissions for file '%s'",
+ fname);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+do_configure(config_object_t *config, const char *config_file)
+{
+ FILE *fp = NULL;
+ char message[80];
+ char tmp_filename[4096];
+ int tmp_fd = -1;
+ mode_t old_umask;
+
+ if (sc_parse(config, config_file) != 0) {
+ printf("Parsing of %s failed.\n", config_file);
+ if (yesno("Start from scratch", 0) == 0) {
+ return 1;
+ }
+ }
+
+ plugin_path_configure(config);
+ listener_configure(config);
+ backend_configure(config);
+
+ printf("\nConfiguration complete.\n\n");
+
+ printf("=== Begin Configuration ===\n");
+ sc_dump(config, stdout);
+ printf("=== End Configuration ===\n");
+
+ snprintf(message, sizeof(message), "Replace %s with the above",
+ config_file);
+ if (yesno(message, 0) == 0) {
+ return 1;
+ }
+
+ snprintf(tmp_filename, sizeof(tmp_filename),
+ "%s.XXXXXX", config_file);
+
+ old_umask = umask(077);
+ tmp_fd = mkstemp(tmp_filename);
+ umask(old_umask);
+
+ if (tmp_fd < 0) {
+ perror("fopen");
+ printf("Failed to write configuration file!\n");
+ return 1;
+ }
+
+ fp = fdopen(tmp_fd, "w+");
+ if (fp == NULL)
+ goto out_fail;
+
+ sc_dump(config, fp);
+
+ if (rename(tmp_filename, config_file) < 0) {
+ perror("rename");
+ goto out_fail;
+ }
+
+ fclose(fp);
+ close(tmp_fd);
+
+ return 0;
+
+out_fail:
+ if (fp)
+ fclose(fp);
+ if (tmp_fd >= 0)
+ close(tmp_fd);
+ if (strlen(tmp_filename))
+ unlink(tmp_filename);
+ printf("Failed to write configuration file!\n");
+ return 1;
+}
diff --git a/agents/virt/server/cpg-virt.c b/agents/virt/server/cpg-virt.c
new file mode 100644
index 0000000..304519c
--- /dev/null
+++ b/agents/virt/server/cpg-virt.c
@@ -0,0 +1,643 @@
+/*
+ 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.
+*/
+/*
+ * Author: Ryan McCabe <rmccabe@redhat.com>
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <time.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <corosync/cpg.h>
+
+#include "debug.h"
+#include "virt.h"
+#include "xvm.h"
+#include "cpg.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+
+#define NAME "cpg"
+#define CPG_VERSION "0.1"
+
+#define MAGIC 0x38e93fc2
+
+struct cpg_info {
+ int magic;
+ config_object_t *config;
+ int vp_count;
+ virConnectPtr *vp;
+};
+
+#define VALIDATE(arg) \
+do {\
+ if (!arg || ((struct cpg_info *) arg)->magic != MAGIC) { \
+ errno = EINVAL;\
+ return -1; \
+ } \
+} while(0)
+
+static struct cpg_info *cpg_virt_handle = NULL;
+static int use_uuid = 0;
+pthread_mutex_t local_vm_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static virt_list_t *local_vm_list = NULL;
+
+pthread_mutex_t remote_vm_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static virt_list_t *remote_vm_list = NULL;
+
+static void cpg_virt_init_libvirt(struct cpg_info *info);
+
+static int
+virt_list_update(struct cpg_info *info, virt_list_t **vl, int my_id)
+{
+ virt_list_t *list = NULL;
+
+ if (*vl)
+ vl_free(*vl);
+
+ list = vl_get(info->vp, info->vp_count, my_id);
+ if (!list && (errno == EPIPE || errno == EINVAL)) {
+ do {
+ cpg_virt_init_libvirt(info);
+ } while (info->vp_count == 0);
+ list = vl_get(info->vp, info->vp_count, my_id);
+ }
+
+ *vl = list;
+ if (!list)
+ return -1;
+
+ return 0;
+}
+
+
+static void
+store_domains(virt_list_t *vl)
+{
+ int i;
+
+ if (!vl)
+ return;
+
+ for (i = 0 ; i < vl->vm_count ; i++) {
+ int ret;
+
+ if (!strcmp(DOMAIN0NAME, vl->vm_states[i].v_name))
+ continue;
+
+ ret = cpg_send_vm_state(&vl->vm_states[i]);
+ if (ret < 0) {
+ printf("Error storing VM state for %s|%s\n",
+ vl->vm_states[i].v_name, vl->vm_states[i].v_uuid);
+ }
+ }
+}
+
+
+static void
+update_local_vms(struct cpg_info *info)
+{
+ uint32_t my_id = 0;
+
+ if (!info)
+ return;
+
+ cpg_get_ids(&my_id, NULL);
+ virt_list_update(info, &local_vm_list, my_id);
+ store_domains(local_vm_list);
+}
+
+
+static int
+do_off(struct cpg_info *info, const char *vm_name)
+{
+ dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name);
+ return vm_off(info->vp, info->vp_count, vm_name);
+
+}
+
+static int
+do_on(struct cpg_info *info, const char *vm_name)
+{
+ dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name);
+ return vm_on(info->vp, info->vp_count, vm_name);
+
+}
+
+static int
+do_reboot(struct cpg_info *info, const char *vm_name)
+{
+ dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name);
+ return vm_reboot(info->vp, info->vp_count, vm_name);
+}
+
+static void
+cpg_join_cb(const struct cpg_address *join, size_t joinlen) {
+ struct cpg_info *info = cpg_virt_handle;
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+}
+
+static void
+cpg_leave_cb(const struct cpg_address *left, size_t leftlen) {
+ struct cpg_info *info = cpg_virt_handle;
+ int i;
+
+ pthread_mutex_lock(&remote_vm_list_lock);
+ for (i = 0 ; i < leftlen ; i++) {
+ dbg_printf(2, "Removing VMs owned by nodeid %u\n", left[i].nodeid);
+ vl_remove_by_owner(&remote_vm_list, left[i].nodeid);
+ }
+ pthread_mutex_unlock(&remote_vm_list_lock);
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+}
+
+static void
+store_cb(void *data, size_t len, uint32_t nodeid, uint32_t seqno)
+{
+ uint32_t my_id;
+ virt_state_t *vs = (virt_state_t *) data;
+ struct cpg_info *info = cpg_virt_handle;
+
+ cpg_get_ids(&my_id, NULL);
+
+ if (nodeid == my_id)
+ return;
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ if (!local_vm_list)
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ pthread_mutex_lock(&remote_vm_list_lock);
+ vl_update(&remote_vm_list, vs);
+ pthread_mutex_unlock(&remote_vm_list_lock);
+}
+
+/*
+** This function must a send reply from at least one node, otherwise
+** the requesting fence_virtd will block forever in wait_cpt_reply.
+*/
+static void
+do_real_work(void *data, size_t len, uint32_t nodeid, uint32_t seqno)
+{
+ struct cpg_info *info = cpg_virt_handle;
+ struct cpg_fence_req *req = data;
+ struct cpg_fence_req reply;
+ int reply_code = -1;
+ virt_state_t *vs = NULL;
+ int cur_state;
+ uint32_t cur_owner = 0;
+ int local = 0;
+ uint32_t my_id, high_id;
+
+ dbg_printf(2, "Request %d for VM %s\n", req->request, req->vm_name);
+
+ if (cpg_get_ids(&my_id, &high_id) == -1) {
+ syslog(LOG_WARNING, "Unable to get CPG IDs");
+ printf("Should never happen: Can't get CPG node ids - can't proceed\n");
+ return;
+ }
+
+ memcpy(&reply, req, sizeof(reply));
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ if (strlen(req->vm_name)) {
+ if (use_uuid)
+ vs = vl_find_uuid(local_vm_list, req->vm_name);
+ else
+ vs = vl_find_name(local_vm_list, req->vm_name);
+
+ if (vs) {
+ local = 1;
+ cur_owner = vs->v_state.s_owner;
+ cur_state = vs->v_state.s_state;
+ dbg_printf(2, "Found VM %s locally state %d\n",
+ req->vm_name, cur_state);
+ }
+ }
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ if (vs == NULL) {
+ pthread_mutex_lock(&remote_vm_list_lock);
+ if (strlen(req->vm_name)) {
+ if (use_uuid)
+ vs = vl_find_uuid(remote_vm_list, req->vm_name);
+ else
+ vs = vl_find_name(remote_vm_list, req->vm_name);
+
+ if (vs) {
+ cur_owner = vs->v_state.s_owner;
+ cur_state = vs->v_state.s_state;
+ dbg_printf(2, "Found VM %s remotely on %u state %d\n",
+ req->vm_name, cur_owner, cur_state);
+ }
+ }
+ pthread_mutex_unlock(&remote_vm_list_lock);
+ }
+
+ if (!vs) {
+ /*
+ ** We know about all domains on all nodes in the CPG group.
+ ** If we didn't find it, and we're high ID, act on the request.
+ ** We can safely assume the VM is OFF because it wasn't found
+ ** on any current members of the CPG group.
+ */
+ if (my_id == high_id) {
+ if (req->request == FENCE_STATUS)
+ reply_code = RESP_OFF;
+ else if (req->request == FENCE_OFF || req->request == FENCE_REBOOT)
+ reply_code = RESP_SUCCESS;
+ else
+ reply_code = 1;
+
+ dbg_printf(2, "Acting on request %d for unknown domain %s -> %d\n",
+ req->request, req->vm_name, reply_code);
+ goto out;
+ }
+
+ dbg_printf(2, "Not acting on request %d for unknown domain %s\n",
+ req->request, req->vm_name);
+ return;
+ }
+
+ if (local) {
+ if (req->request == FENCE_STATUS) {
+ /* We already have the status */
+ if (cur_state == VIR_DOMAIN_SHUTOFF)
+ reply_code = RESP_OFF;
+ else
+ reply_code = RESP_SUCCESS;
+ } else if (req->request == FENCE_OFF) {
+ reply_code = do_off(info, req->vm_name);
+ } else if (req->request == FENCE_ON) {
+ reply_code = do_on(info, req->vm_name);
+ } else if (req->request == FENCE_REBOOT) {
+ reply_code = do_reboot(info, req->vm_name);
+ } else {
+ dbg_printf(2, "Not explicitly handling request type %d for %s\n",
+ req->request, req->vm_name);
+ reply_code = 0;
+ }
+ goto out;
+ }
+
+ /*
+ ** This is a request for a non-local domain that exists on a
+ ** current CPG group member, so that member will see the request
+ ** and act on it. We don't need to do anything.
+ */
+ dbg_printf(2, "Nothing to do for non-local domain %s seq %d owner %u\n",
+ req->vm_name, seqno, cur_owner);
+ return;
+
+out:
+ dbg_printf(2, "[%s] sending reply code seq %d -> %d\n",
+ req->vm_name, seqno, reply_code);
+
+ reply.response = reply_code;
+ if (cpg_send_reply(&reply, sizeof(reply), nodeid, seqno) < 0) {
+ dbg_printf(2, "cpg_send_reply failed for %s [%d %d]: %s\n",
+ req->vm_name, nodeid, seqno, strerror(errno));
+ }
+}
+
+
+static int
+do_request(const char *vm_name, int request, uint32_t seqno)
+{
+ struct cpg_fence_req freq, *frp;
+ size_t retlen;
+ uint32_t seq;
+ int ret;
+
+ memset(&freq, 0, sizeof(freq));
+ if (!vm_name) {
+ dbg_printf(1, "No VM name\n");
+ return 1;
+ }
+
+ if (strlen(vm_name) >= sizeof(freq.vm_name)) {
+ dbg_printf(1, "VM name %s too long\n", vm_name);
+ return 1;
+ }
+
+ strcpy(freq.vm_name, vm_name);
+
+ freq.request = request;
+ freq.seqno = seqno;
+
+ if (cpg_send_req(&freq, sizeof(freq), &seq) != 0) {
+ dbg_printf(1, "Failed to send request %d for VM %s\n",
+ freq.request, vm_name);
+ return 1;
+ }
+
+ dbg_printf(2, "Sent request %d for VM %s got seqno %d\n",
+ request, vm_name, seq);
+
+ if (cpg_wait_reply((void *) &frp, &retlen, seq) != 0) {
+ dbg_printf(1, "Failed to receive reply seq %d for %s\n", seq, vm_name);
+ return 1;
+ }
+
+ dbg_printf(2, "Received reply [%d] seq %d for %s\n",
+ frp->response, seq, vm_name);
+
+ ret = frp->response;
+ free(frp);
+
+ return ret;
+}
+
+
+static int
+cpg_virt_null(const char *vm_name, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] Null operation on %s\n", vm_name);
+
+ return 1;
+}
+
+
+static int
+cpg_virt_off(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] OFF operation on %s seq %d\n", vm_name, seqno);
+
+ return do_request(vm_name, FENCE_OFF, seqno);
+}
+
+
+static int
+cpg_virt_on(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] ON operation on %s seq %d\n", vm_name, seqno);
+
+ return do_request(vm_name, FENCE_ON, seqno);
+}
+
+
+static int
+cpg_virt_devstatus(void *priv)
+{
+ printf("[cpg-virt] Device status\n");
+ VALIDATE(priv);
+
+ return 0;
+}
+
+
+static int
+cpg_virt_status(const char *vm_name, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] STATUS operation on %s\n", vm_name);
+
+ return do_request(vm_name, FENCE_STATUS, 0);
+}
+
+
+static int
+cpg_virt_reboot(const char *vm_name, const char *src,
+ uint32_t seqno, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] REBOOT operation on %s seq %d\n", vm_name, seqno);
+
+ return do_request(vm_name, FENCE_REBOOT, 0);
+}
+
+
+static int
+cpg_virt_hostlist(hostlist_callback callback, void *arg, void *priv)
+{
+ struct cpg_info *info = (struct cpg_info *) priv;
+ int i;
+
+ VALIDATE(priv);
+ printf("[cpg-virt] HOSTLIST operation\n");
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ for (i = 0 ; i < local_vm_list->vm_count ; i++) {
+ callback(local_vm_list->vm_states[i].v_name,
+ local_vm_list->vm_states[i].v_uuid,
+ local_vm_list->vm_states[i].v_state.s_state, arg);
+ }
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ return 1;
+}
+
+static void
+cpg_virt_init_libvirt(struct cpg_info *info) {
+ config_object_t *config = info->config;
+ int i = 0;
+
+ if (info->vp) {
+ dbg_printf(2, "Lost libvirtd connection. Reinitializing.\n");
+ for (i = 0 ; i < info->vp_count ; i++)
+ virConnectClose(info->vp[i]);
+ free(info->vp);
+ info->vp = NULL;
+ }
+ info->vp_count = 0;
+
+ do {
+ virConnectPtr vp;
+ virConnectPtr *vpl = NULL;
+ char conf_attr[256];
+ char value[1024];
+ char *uri;
+
+ if (i != 0) {
+ snprintf(conf_attr, sizeof(conf_attr),
+ "backends/cpg/@uri%d", i);
+ } else
+ snprintf(conf_attr, sizeof(conf_attr), "backends/cpg/@uri");
+ ++i;
+
+ if (sc_get(config, conf_attr, value, sizeof(value)) != 0)
+ break;
+
+ uri = value;
+ vp = virConnectOpen(uri);
+ if (!vp) {
+ dbg_printf(1, "[cpg-virt:INIT] Failed to connect to URI: %s\n", uri);
+ continue;
+ }
+
+ vpl = realloc(info->vp, sizeof(*info->vp) * (info->vp_count + 1));
+ if (!vpl) {
+ dbg_printf(1, "[cpg-virt:INIT] Out of memory allocating URI: %s\n",
+ uri);
+ virConnectClose(vp);
+ continue;
+ }
+
+ info->vp = vpl;
+ info->vp[info->vp_count++] = vp;
+
+ if (i > 1)
+ dbg_printf(1, "[cpg-virt:INIT] Added URI%d %s\n", i - 1, uri);
+ else
+ dbg_printf(1, "[cpg_virt:INIT] Added URI %s\n", uri);
+ } while (1);
+}
+
+static int
+cpg_virt_init(backend_context_t *c, config_object_t *config)
+{
+ char value[1024];
+ struct cpg_info *info = NULL;
+ int ret;
+
+ ret = cpg_start(PACKAGE_NAME,
+ do_real_work, store_cb, cpg_join_cb, cpg_leave_cb);
+ if (ret < 0)
+ return -1;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+ info->magic = MAGIC;
+ info->config = config;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0)
+ dset(atoi(value));
+
+ cpg_virt_init_libvirt(info);
+
+ /* Naming scheme is no longer a top-level config option.
+ * However, we retain it here for configuration compatibility with
+ * versions 0.1.3 and previous.
+ */
+ if (sc_get(config, "fence_virtd/@name_mode",
+ value, sizeof(value)-1) == 0) {
+
+ dbg_printf(1, "Got %s for name_mode\n", value);
+ if (!strcasecmp(value, "uuid")) {
+ use_uuid = 1;
+ } else if (!strcasecmp(value, "name")) {
+ use_uuid = 0;
+ } else {
+ dbg_printf(1, "Unsupported name_mode: %s\n", value);
+ }
+ }
+
+ if (sc_get(config, "backends/cpg/@name_mode",
+ value, sizeof(value)-1) == 0)
+ {
+ dbg_printf(1, "Got %s for name_mode\n", value);
+ if (!strcasecmp(value, "uuid")) {
+ use_uuid = 1;
+ } else if (!strcasecmp(value, "name")) {
+ use_uuid = 0;
+ } else {
+ dbg_printf(1, "Unsupported name_mode: %s\n", value);
+ }
+ }
+
+ if (info->vp_count < 1) {
+ dbg_printf(1, "[cpg_virt:INIT] Could not connect to any hypervisors\n");
+ cpg_stop();
+ free(info);
+ return -1;
+ }
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ *c = (void *) info;
+ cpg_virt_handle = info;
+ return 0;
+}
+
+
+static int
+cpg_virt_shutdown(backend_context_t c)
+{
+ struct cpg_info *info = (struct cpg_info *)c;
+ int i = 0;
+ int ret = 0;
+
+ VALIDATE(info);
+ info->magic = 0;
+
+ cpg_stop();
+
+ for (i = 0 ; i < info->vp_count ; i++) {
+ if (virConnectClose(info->vp[i]) < 0)
+ ret = -errno;
+ }
+
+ free(info->vp);
+ free(info);
+
+ return ret;
+}
+
+
+static fence_callbacks_t cpg_callbacks = {
+ .null = cpg_virt_null,
+ .off = cpg_virt_off,
+ .on = cpg_virt_on,
+ .reboot = cpg_virt_reboot,
+ .status = cpg_virt_status,
+ .devstatus = cpg_virt_devstatus,
+ .hostlist = cpg_virt_hostlist
+};
+
+static backend_plugin_t cpg_virt_plugin = {
+ .name = NAME,
+ .version = CPG_VERSION,
+ .callbacks = &cpg_callbacks,
+ .init = cpg_virt_init,
+ .cleanup = cpg_virt_shutdown,
+};
+
+double
+BACKEND_VER_SYM(void)
+{
+ return PLUGIN_VERSION_BACKEND;
+}
+
+const backend_plugin_t *
+BACKEND_INFO_SYM(void)
+{
+ return &cpg_virt_plugin;
+}
diff --git a/agents/virt/server/cpg.c b/agents/virt/server/cpg.c
new file mode 100644
index 0000000..8f84cd8
--- /dev/null
+++ b/agents/virt/server/cpg.c
@@ -0,0 +1,411 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <list.h>
+#include <pthread.h>
+
+#include <corosync/cpg.h>
+
+#include "debug.h"
+#include "virt.h"
+#include "cpg.h"
+
+#define NODE_ID_NONE ((uint32_t) -1)
+
+struct msg_queue_node {
+ list_head();
+ uint32_t seqno;
+#define STATE_CLEAR 0
+#define STATE_MESSAGE 1
+ uint32_t state;
+ void *msg;
+ size_t msglen;
+};
+
+struct wire_msg {
+#define TYPE_REQUEST 0
+#define TYPE_REPLY 1
+#define TYPE_STORE_VM 2
+ uint32_t type;
+ uint32_t seqno;
+ uint32_t target;
+ uint32_t pad;
+ char data[0];
+};
+
+static uint32_t seqnum = 0;
+static struct msg_queue_node *pending = NULL;
+static cpg_handle_t cpg_handle;
+static struct cpg_name gname;
+
+static pthread_mutex_t cpg_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cpg_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t cpg_thread = 0;
+
+static pthread_mutex_t cpg_ids_mutex = PTHREAD_MUTEX_INITIALIZER;
+static uint32_t my_node_id = NODE_ID_NONE;
+static uint32_t high_id_from_callback = NODE_ID_NONE;
+
+static request_callback_fn req_callback_fn;
+static request_callback_fn store_callback_fn;
+static confchange_callback_fn conf_leave_fn;
+static confchange_callback_fn conf_join_fn;
+
+
+int
+cpg_get_ids(uint32_t *my_id, uint32_t *high_id)
+{
+ if (!my_id && !high_id)
+ return -1;
+
+ pthread_mutex_lock(&cpg_ids_mutex);
+ if (my_id)
+ *my_id = my_node_id;
+
+ if (high_id)
+ *high_id = high_id_from_callback;
+ pthread_mutex_unlock(&cpg_ids_mutex);
+
+ return 0;
+}
+
+static void
+cpg_deliver_func(cpg_handle_t h,
+ const struct cpg_name *group_name,
+ uint32_t nodeid,
+ uint32_t pid,
+ void *msg,
+ size_t msglen)
+{
+ struct msg_queue_node *n;
+ struct wire_msg *m = msg;
+ int x, found;
+
+ pthread_mutex_lock(&cpg_mutex);
+ if (m->type == TYPE_REPLY) {
+ /* Reply to a request we sent */
+ found = 0;
+
+ list_for(&pending, n, x) {
+ if (m->seqno != n->seqno)
+ continue;
+ if (m->target != my_node_id)
+ continue;
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ goto out_unlock;
+
+ /* Copy our message in to a buffer */
+ n->msglen = msglen - sizeof(*m);
+ if (!n->msglen) {
+ /* XXX do what? */
+ }
+ n->msg = malloc(n->msglen);
+ if (!n->msg) {
+ goto out_unlock;
+ }
+ n->state = STATE_MESSAGE;
+ memcpy(n->msg, (char *)msg + sizeof(*m), n->msglen);
+
+ list_remove(&pending, n);
+ list_insert(&pending, n);
+
+ dbg_printf(2, "Seqnum %d replied; removing from list\n", n->seqno);
+
+ pthread_cond_broadcast(&cpg_cond);
+ goto out_unlock;
+ }
+ pthread_mutex_unlock(&cpg_mutex);
+
+ if (m->type == TYPE_REQUEST) {
+ req_callback_fn(&m->data, msglen - sizeof(*m),
+ nodeid, m->seqno);
+ }
+ if (m->type == TYPE_STORE_VM) {
+ store_callback_fn(&m->data, msglen - sizeof(*m),
+ nodeid, m->seqno);
+ }
+
+ return;
+
+out_unlock:
+ pthread_mutex_unlock(&cpg_mutex);
+}
+
+
+static void
+cpg_config_change(cpg_handle_t h,
+ const struct cpg_name *group_name,
+ const struct cpg_address *members, size_t memberlen,
+ const struct cpg_address *left, size_t leftlen,
+ const struct cpg_address *join, size_t joinlen)
+{
+ int x;
+ int high;
+
+ pthread_mutex_lock(&cpg_ids_mutex);
+ high = my_node_id;
+
+ for (x = 0; x < memberlen; x++) {
+ if (members[x].nodeid > high)
+ high = members[x].nodeid;
+ }
+
+ high_id_from_callback = high;
+ pthread_mutex_unlock(&cpg_ids_mutex);
+
+ if (joinlen > 0)
+ conf_join_fn(join, joinlen);
+
+ if (leftlen > 0)
+ conf_leave_fn(left, leftlen);
+}
+
+
+static cpg_callbacks_t my_callbacks = {
+ .cpg_deliver_fn = cpg_deliver_func,
+ .cpg_confchg_fn = cpg_config_change
+};
+
+
+int
+cpg_send_req(void *data, size_t len, uint32_t *seqno)
+{
+ struct iovec iov;
+ struct msg_queue_node *n;
+ struct wire_msg *m;
+ size_t msgsz = sizeof(*m) + len;
+ int ret;
+
+ n = malloc(sizeof(*n));
+ if (!n)
+ return -1;
+
+ m = malloc(msgsz);
+ if (!m) {
+ free(n);
+ return -1;
+ }
+
+ /* only incremented on send */
+ n->state = STATE_CLEAR;
+ n->msg = NULL;
+ n->msglen = 0;
+
+ pthread_mutex_lock(&cpg_mutex);
+ list_insert(&pending, n);
+ n->seqno = ++seqnum;
+ m->seqno = seqnum;
+ *seqno = seqnum;
+ pthread_mutex_unlock(&cpg_mutex);
+
+ m->type = TYPE_REQUEST; /* XXX swab? */
+ m->target = NODE_ID_NONE;
+ memcpy(&m->data, data, len);
+
+ iov.iov_base = m;
+ iov.iov_len = msgsz;
+ ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1);
+
+ free(m);
+ if (ret == CS_OK)
+ return 0;
+ return -1;
+}
+
+
+int
+cpg_send_vm_state(virt_state_t *vs)
+{
+ struct iovec iov;
+ struct msg_queue_node *n;
+ struct wire_msg *m;
+ size_t msgsz = sizeof(*m) + sizeof(*vs);
+ int ret;
+
+ n = calloc(1, (sizeof(*n)));
+ if (!n)
+ return -1;
+
+ m = calloc(1, msgsz);
+ if (!m) {
+ free(n);
+ return -1;
+ }
+
+ n->state = STATE_MESSAGE;
+ n->msg = NULL;
+ n->msglen = 0;
+
+ pthread_mutex_lock(&cpg_mutex);
+ list_insert(&pending, n);
+ pthread_mutex_unlock(&cpg_mutex);
+
+ m->type = TYPE_STORE_VM;
+ m->target = NODE_ID_NONE;
+
+ memcpy(&m->data, vs, sizeof(*vs));
+
+ iov.iov_base = m;
+ iov.iov_len = msgsz;
+ ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1);
+
+ free(m);
+ if (ret == CS_OK)
+ return 0;
+
+ return -1;
+}
+
+
+int
+cpg_send_reply(void *data, size_t len, uint32_t nodeid, uint32_t seqno)
+{
+ struct iovec iov;
+ struct wire_msg *m;
+ size_t msgsz = sizeof(*m) + len;
+ int ret;
+
+ m = malloc(msgsz);
+ if (!m)
+ return -1;
+
+ /* only incremented on send */
+ m->seqno = seqno;
+ m->type = TYPE_REPLY; /* XXX swab? */
+ m->target = nodeid;
+ memcpy(&m->data, data, len);
+
+ iov.iov_base = m;
+ iov.iov_len = msgsz;
+ ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1);
+
+ free(m);
+ if (ret == CS_OK)
+ return 0;
+
+ return -1;
+}
+
+
+int
+cpg_wait_reply(void **data, size_t *len, uint32_t seqno)
+{
+ struct msg_queue_node *n;
+ int x, found = 0;
+
+ while (!found) {
+ found = 0;
+ pthread_mutex_lock(&cpg_mutex);
+ pthread_cond_wait(&cpg_cond, &cpg_mutex);
+
+ list_for(&pending, n, x) {
+ if (n->seqno != seqno)
+ continue;
+ if (n->state != STATE_MESSAGE)
+ continue;
+ found = 1;
+ goto out;
+ }
+ pthread_mutex_unlock(&cpg_mutex);
+ }
+
+out:
+ list_remove(&pending, n);
+ pthread_mutex_unlock(&cpg_mutex);
+
+ *data = n->msg;
+ *len = n->msglen;
+ free(n);
+
+ return 0;
+}
+
+
+static void *
+cpg_dispatch_thread(void *arg)
+{
+ cpg_dispatch(cpg_handle, CS_DISPATCH_BLOCKING);
+
+ return NULL;
+}
+
+
+int
+cpg_start( const char *name,
+ request_callback_fn req_cb_fn,
+ request_callback_fn store_cb_fn,
+ confchange_callback_fn join_fn,
+ confchange_callback_fn leave_fn)
+{
+ cpg_handle_t h;
+ int ret;
+
+ errno = EINVAL;
+
+ if (!name)
+ return -1;
+
+ ret = snprintf(gname.value, sizeof(gname.value), "%s", name);
+ if (ret <= 0)
+ return -1;
+
+ if (ret >= sizeof(gname.value)) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ gname.length = ret;
+
+ memset(&h, 0, sizeof(h));
+ if (cpg_initialize(&h, &my_callbacks) != CS_OK) {
+ perror("cpg_initialize");
+ return -1;
+ }
+
+ if (cpg_join(h, &gname) != CS_OK) {
+ perror("cpg_join");
+ return -1;
+ }
+
+ cpg_local_get(h, &my_node_id);
+ dbg_printf(2, "My CPG nodeid is %d\n", my_node_id);
+
+ pthread_mutex_lock(&cpg_mutex);
+ pthread_create(&cpg_thread, NULL, cpg_dispatch_thread, NULL);
+
+ memcpy(&cpg_handle, &h, sizeof(h));
+
+ req_callback_fn = req_cb_fn;
+ store_callback_fn = store_cb_fn;
+ conf_join_fn = join_fn;
+ conf_leave_fn = leave_fn;
+
+ pthread_mutex_unlock(&cpg_mutex);
+
+ return 0;
+}
+
+
+int
+cpg_stop(void)
+{
+ pthread_cancel(cpg_thread);
+ pthread_join(cpg_thread, NULL);
+ cpg_leave(cpg_handle, &gname);
+ cpg_finalize(cpg_handle);
+
+ return 0;
+}
diff --git a/agents/virt/server/cpg.h b/agents/virt/server/cpg.h
new file mode 100644
index 0000000..6873955
--- /dev/null
+++ b/agents/virt/server/cpg.h
@@ -0,0 +1,29 @@
+#ifndef __FENCE_VIRTD_CPG_H
+#define __FENCE_VIRTD_CPG_H
+
+struct cpg_fence_req {
+ char vm_name[128];
+ int request;
+ uint32_t seqno;
+ uint32_t response;
+};
+
+typedef void (*request_callback_fn)(void *data, size_t len, uint32_t nodeid,
+ uint32_t seqno);
+typedef void (*confchange_callback_fn)(const struct cpg_address *m, size_t len);
+
+int cpg_start( const char *name,
+ request_callback_fn func,
+ request_callback_fn store_func,
+ confchange_callback_fn join,
+ confchange_callback_fn leave);
+
+int cpg_get_ids(uint32_t *me, uint32_t *high);
+int cpg_stop(void);
+int cpg_send_req(void *data, size_t len, uint32_t *seqno);
+int cpg_wait_reply(void **data, size_t *len, uint32_t seqno);
+int cpg_send_reply(void *data, size_t len, uint32_t nodeid, uint32_t seqno);
+int cpg_send_vm_state(virt_state_t *vs);
+
+
+#endif
diff --git a/agents/virt/server/daemon_init.c b/agents/virt/server/daemon_init.c
new file mode 100644
index 0000000..29b33ad
--- /dev/null
+++ b/agents/virt/server/daemon_init.c
@@ -0,0 +1,215 @@
+/** @file
+ * daemon_init function, does sanity checks and calls daemon().
+ *
+ * Author: Jeff Moyer <jmoyer@redhat.com>
+ */
+/*
+ * TODO: Clean this up so that only one function constructs the
+ * pidfile /var/run/loggerd.PID, and perhaps only one function
+ * forms the /proc/PID/ path.
+ *
+ * Also need to add file locking for the pid file.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <libgen.h>
+#include <signal.h>
+#include <syslog.h>
+
+
+/*
+ * This should ultimately go in a header file.
+ */
+void daemon_init(const char *prog, const char *pid_file, int nofork);
+void daemon_cleanup(void);
+int check_process_running(const char *cmd, const char *pid_file, pid_t * pid);
+
+/*
+ * Local prototypes.
+ */
+static void update_pidfile(const char *filename);
+static int setup_sigmask(void);
+static char pid_filename[PATH_MAX];
+
+static int
+check_pid_valid(pid_t pid, const char *prog)
+{
+ FILE *fp;
+ DIR *dir;
+ char filename[PATH_MAX];
+ char dirpath[PATH_MAX];
+ char proc_cmdline[64]; /* yank this from kernel somewhere */
+ char *s = NULL;
+
+ memset(filename, 0, PATH_MAX);
+ memset(dirpath, 0, PATH_MAX);
+
+ snprintf(dirpath, sizeof (dirpath), "/proc/%d", pid);
+ if ((dir = opendir(dirpath)) == NULL) {
+ return 0; /* Pid has gone away. */
+ }
+ closedir(dir);
+
+ /*
+ * proc-pid directory exists. Now check to see if this
+ * PID corresponds to the daemon we want to start.
+ */
+ snprintf(filename, sizeof (filename), "/proc/%d/cmdline", pid);
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ perror("check_pid_valid");
+ return 0; /* Who cares.... Let's boogy on. */
+ }
+
+ if (!fgets(proc_cmdline, sizeof (proc_cmdline) - 1, fp)) {
+ /*
+ * Okay, we've seen processes keep a reference to a
+ * /proc/PID/stat file and not let go. Then when
+ * you try to read /proc/PID/cmline, you get either
+ * \000 or -1. In either case, we can safely assume
+ * the process has gone away.
+ */
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ s = &(proc_cmdline[strlen(proc_cmdline)]);
+ if (*s == '\n')
+ *s = 0;
+
+ /*
+ * Check to see if this is the same executable.
+ */
+ if (strstr(proc_cmdline, prog) == NULL) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+int
+check_process_running(const char *cmd, const char *filename, pid_t * pid)
+{
+ pid_t oldpid;
+ FILE *fp = NULL;
+ int ret;
+
+ *pid = -1;
+
+ /*
+ * Read the pid from the file.
+ */
+ fp = fopen(filename, "r");
+ if (fp == NULL) { /* error */
+ return 0;
+ }
+
+ ret = fscanf(fp, "%d\n", &oldpid);
+ fclose(fp);
+
+ if ((ret == EOF) || (ret != 1))
+ return 0;
+
+ if (check_pid_valid(oldpid, cmd)) {
+ *pid = oldpid;
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+update_pidfile(const char *filename)
+{
+ FILE *fp = NULL;
+
+ strncpy(pid_filename, filename, PATH_MAX - 1);
+
+ fp = fopen(pid_filename, "w");
+ if (fp == NULL) {
+ syslog(LOG_ERR, "daemon_init: Unable to create pidfile %s: %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+
+ fprintf(fp, "%d", getpid());
+ fclose(fp);
+}
+
+
+static int
+setup_sigmask(void)
+{
+ sigset_t set;
+
+ sigfillset(&set);
+
+ /*
+ * Dont't block signals which would cause us to dump core.
+ */
+ sigdelset(&set, SIGQUIT);
+ sigdelset(&set, SIGILL);
+ sigdelset(&set, SIGTRAP);
+ sigdelset(&set, SIGABRT);
+ sigdelset(&set, SIGFPE);
+ sigdelset(&set, SIGSEGV);
+ sigdelset(&set, SIGBUS);
+
+ /*
+ * Don't block SIGTERM or SIGCHLD
+ */
+ sigdelset(&set, SIGTERM);
+ sigdelset(&set, SIGINT);
+ sigdelset(&set, SIGQUIT);
+ sigdelset(&set, SIGCHLD);
+
+ return (sigprocmask(SIG_BLOCK, &set, NULL));
+}
+
+
+void
+daemon_init(const char *prog, const char *pid_file, int nofork)
+{
+ pid_t pid;
+
+ if (check_process_running(prog, pid_file, &pid) && (pid != getpid())) {
+ syslog(LOG_ERR,
+ "daemon_init: Process \"%s\" already running.\n",
+ prog);
+ exit(1);
+ }
+
+ if (setup_sigmask() < 0) {
+ syslog(LOG_ERR, "daemon_init: Unable to set signal mask.\n");
+ exit(1);
+ }
+
+ if (!nofork && daemon(0, 0)) {
+ syslog(LOG_ERR, "daemon_init: Unable to daemonize.\n");
+ exit(1);
+ }
+
+ update_pidfile(pid_file);
+}
+
+
+void
+daemon_cleanup(void)
+{
+ if (strlen(pid_filename))
+ unlink(pid_filename);
+}
diff --git a/agents/virt/server/history.c b/agents/virt/server/history.c
new file mode 100644
index 0000000..bd3a68c
--- /dev/null
+++ b/agents/virt/server/history.c
@@ -0,0 +1,124 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <list.h>
+#include <time.h>
+
+#include "history.h"
+
+history_info_t *
+history_init(history_compare_fn func, time_t expiration, size_t element_size)
+{
+ history_info_t *hist;
+
+ errno = EINVAL;
+ if (!func || !expiration || !element_size)
+ return NULL;
+
+ hist = malloc(sizeof(*hist));
+ if (!hist)
+ return NULL;
+ memset(hist, 0, sizeof(*hist));
+
+ hist->timeout = expiration;
+ hist->element_size = element_size;
+ hist->compare_func = func;
+
+ return hist;
+}
+
+
+/*
+ * Purge our history when the entries time out.
+ *
+ * Returns 1 if a matching history node was found, or 0
+ * if not.
+ */
+int
+history_check(history_info_t *hinfo, void *stuff)
+{
+ history_node *entry = NULL;
+ time_t now;
+ int x;
+
+ if (!hinfo)
+ return 0; /* XXX */
+
+ if (!hinfo->hist)
+ return 0;
+
+ now = time(NULL);
+
+loop_again:
+ list_for((&hinfo->hist), entry, x) {
+ if (entry->when < (now - hinfo->timeout)) {
+ list_remove((&hinfo->hist), entry);
+ free(entry->data);
+ free(entry);
+ goto loop_again;
+ }
+
+ if (hinfo->compare_func(entry->data, stuff)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+int
+history_record(history_info_t *hinfo, void *data)
+{
+ history_node *entry = NULL;
+
+ errno = EINVAL;
+ if (!data || !hinfo)
+ return -1;
+
+ if (history_check(hinfo, data) == 1) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ entry = malloc(sizeof(*entry));
+ if (!entry) {
+ return -1;
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ entry->data = malloc(hinfo->element_size);
+ if (!entry->data) {
+ free(entry);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(entry->data, data, hinfo->element_size);
+ entry->when = time(NULL);
+ list_insert((&hinfo->hist), entry);
+ return 0;
+}
+
+
+int
+history_wipe(history_info_t *hinfo)
+{
+ history_node *entry = NULL;
+
+ if (!hinfo)
+ return -1;
+
+ while (hinfo->hist) {
+ entry = hinfo->hist;
+ list_remove((&hinfo->hist), entry);
+ free(entry->data);
+ free(entry);
+ }
+
+ /* User must free(hinfo); */
+ return 0;
+}
diff --git a/agents/virt/server/libvirt.c b/agents/virt/server/libvirt.c
new file mode 100644
index 0000000..8f01045
--- /dev/null
+++ b/agents/virt/server/libvirt.c
@@ -0,0 +1,359 @@
+/*
+ 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 <string.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 <libvirt/virterror.h>
+#include <nss.h>
+#include <libgen.h>
+#include <syslog.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "virt.h"
+#include "debug.h"
+#include "uuid-test.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+
+#define NAME "libvirt"
+#define LIBVIRT_VERSION "0.3"
+
+#define MAGIC 0x1e19317a
+
+struct libvirt_info {
+ int magic;
+ config_object_t *config;
+ int vp_count;
+ virConnectPtr *vp;
+};
+
+#define VALIDATE(arg) \
+do {\
+ if (!arg || ((struct libvirt_info *)arg)->magic != MAGIC) { \
+ errno = EINVAL;\
+ return -1; \
+ } \
+} while(0)
+
+
+static void
+libvirt_init_libvirt_conf(struct libvirt_info *info) {
+ config_object_t *config = info->config;
+ int i = 0;
+
+ if (info->vp) {
+ dbg_printf(2, "Lost libvirtd connection. Reinitializing.\n");
+ for (i = 0 ; i < info->vp_count ; i++)
+ virConnectClose(info->vp[i]);
+ free(info->vp);
+ info->vp = NULL;
+ }
+ info->vp_count = 0;
+
+ do {
+ virConnectPtr vp;
+ virConnectPtr *vpl = NULL;
+ char conf_attr[256];
+ char value[1024];
+ char *uri;
+
+ if (i != 0) {
+ snprintf(conf_attr, sizeof(conf_attr),
+ "backends/libvirt/@uri%d", i);
+ } else
+ snprintf(conf_attr, sizeof(conf_attr), "backends/libvirt/@uri");
+ ++i;
+
+ if (sc_get(config, conf_attr, value, sizeof(value)) != 0)
+ break;
+
+ uri = value;
+ vp = virConnectOpen(uri);
+ if (!vp) {
+ dbg_printf(1, "[libvirt:INIT] Failed to connect to URI: %s\n", uri);
+ continue;
+ }
+
+ vpl = realloc(info->vp, sizeof(*info->vp) * (info->vp_count + 1));
+ if (!vpl) {
+ dbg_printf(1, "[libvirt:INIT] Out of memory allocating URI: %s\n",
+ uri);
+ virConnectClose(vp);
+ continue;
+ }
+
+ info->vp = vpl;
+ info->vp[info->vp_count++] = vp;
+
+ if (i > 1)
+ dbg_printf(1, "[libvirt:INIT] Added URI%d %s\n", i - 1, uri);
+ else
+ dbg_printf(1, "[libvirt:INIT] Added URI %s\n", uri);
+ } while (1);
+}
+
+
+static int
+libvirt_bad_connections(struct libvirt_info *info) {
+ int bad = 0;
+ int i;
+
+ for (i = 0 ; i < info->vp_count ; i++) {
+ /*
+ ** Send a dummy command to trigger an error if libvirtd
+ ** died or restarted
+ */
+ virConnectNumOfDomains(info->vp[i]);
+ if (!virConnectIsAlive(info->vp[i])) {
+ dbg_printf(1, "libvirt connection %d is dead\n", i);
+ bad++;
+ }
+ }
+
+ if (info->vp_count < 1 || bad)
+ libvirt_init_libvirt_conf(info);
+
+ return bad || info->vp_count < 1;
+}
+
+static void
+libvirt_validate_connections(struct libvirt_info *info) {
+ while (1) {
+ if (libvirt_bad_connections(info))
+ sleep(1);
+ else
+ break;
+ }
+}
+
+static int
+libvirt_null(const char *vm_name, void *priv)
+{
+ dbg_printf(5, "ENTER %s %s\n", __FUNCTION__, vm_name);
+ printf("NULL operation: returning failure\n");
+ return 1;
+}
+
+
+static int
+libvirt_off(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_off(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_on(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_on(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_devstatus(void *priv)
+{
+ dbg_printf(5, "%s ---\n", __FUNCTION__);
+
+ if (priv)
+ return 0;
+ return 1;
+}
+
+
+static int
+libvirt_status(const char *vm_name, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s\n", __FUNCTION__, vm_name);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_status(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_reboot(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_reboot(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_hostlist(hostlist_callback callback, void *arg, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+ virt_list_t *vl;
+ int x;
+
+ dbg_printf(5, "ENTER %s\n", __FUNCTION__);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+
+ vl = vl_get(info->vp, info->vp_count, 1);
+ if (!vl)
+ return 0;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ callback(vl->vm_states[x].v_name,
+ vl->vm_states[x].v_uuid,
+ vl->vm_states[x].v_state.s_state, arg);
+
+ dbg_printf(10, "[libvirt:HOSTLIST] Sent %s %s %d\n",
+ vl->vm_states[x].v_name,
+ vl->vm_states[x].v_uuid,
+ vl->vm_states[x].v_state.s_state);
+ }
+
+ vl_free(vl);
+ return 0;
+}
+
+
+static int
+libvirt_init(backend_context_t *c, config_object_t *config)
+{
+ char value[256];
+ struct libvirt_info *info = NULL;
+
+ dbg_printf(5, "ENTER [%s:%d %s]\n", __FILE__, __LINE__, __FUNCTION__);
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+ info->magic = MAGIC;
+ info->config = config;
+
+ libvirt_init_libvirt_conf(info);
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0)
+ dset(atoi(value));
+
+ if (info->vp_count < 1) {
+ dbg_printf(1, "[libvirt:INIT] Could not connect to any hypervisors\n");
+ if (info->vp)
+ free(info->vp);
+ free(info);
+ return -1;
+ }
+
+ *c = (void *) info;
+ return 0;
+}
+
+
+static int
+libvirt_shutdown(backend_context_t c)
+{
+ struct libvirt_info *info = (struct libvirt_info *)c;
+ int i;
+ int ret = 0;
+
+ VALIDATE(info);
+
+ for (i = 0 ; i < info->vp_count ; i++) {
+ if (virConnectClose(info->vp[i]) < 0)
+ ret = -errno;
+ }
+
+ free(info->vp);
+ free(info);
+ return ret;
+}
+
+
+static fence_callbacks_t libvirt_callbacks = {
+ .null = libvirt_null,
+ .off = libvirt_off,
+ .on = libvirt_on,
+ .reboot = libvirt_reboot,
+ .status = libvirt_status,
+ .devstatus = libvirt_devstatus,
+ .hostlist = libvirt_hostlist
+};
+
+static backend_plugin_t libvirt_plugin = {
+ .name = NAME,
+ .version = LIBVIRT_VERSION,
+ .callbacks = &libvirt_callbacks,
+ .init = libvirt_init,
+ .cleanup = libvirt_shutdown,
+};
+
+double
+BACKEND_VER_SYM(void)
+{
+ return PLUGIN_VERSION_BACKEND;
+}
+
+const backend_plugin_t *
+BACKEND_INFO_SYM(void)
+{
+ return &libvirt_plugin;
+}
diff --git a/agents/virt/server/main.c b/agents/virt/server/main.c
new file mode 100644
index 0000000..fe57899
--- /dev/null
+++ b/agents/virt/server/main.c
@@ -0,0 +1,281 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <syslog.h>
+
+/* Local includes */
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "xvm.h"
+#include "server_plugin.h"
+#include "simple_auth.h"
+#include "debug.h"
+
+/* configure.c */
+int daemon_init(const char *prog, const char *pid_file, int nofork);
+int daemon_cleanup(void);
+
+
+static void
+usage(void)
+{
+ printf("Usage: fence_virtd [options]\n");
+ printf(" -F Do not daemonize.\n");
+ printf(" -f <file> Use <file> as configuration file.\n");
+ printf(" -d <level> Set debugging level to <level>.\n");
+ printf(" -c Configuration mode.\n");
+ printf(" -l List plugins.\n");
+ printf(" -w Wait for initialization.\n");
+ printf(" -p <file> Use <file> to record the active process id.\n");
+}
+
+
+static int run = 1;
+static void
+exit_handler(int sig)
+{
+ run = 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ char val[4096];
+ char listener_name[80];
+ char backend_name[80];
+ const char *config_file = SYSCONFDIR "/fence_virt.conf";
+ char *pid_file = NULL;
+ config_object_t *config = NULL;
+ map_object_t *map = NULL;
+ const listener_plugin_t *lp;
+ const backend_plugin_t *p;
+ listener_context_t listener_ctx = NULL;
+ backend_context_t backend_ctx = NULL;
+ int debug_set = 0, foreground = 0, wait_for_init = 0;
+ int opt, configure = 0;
+
+ config = sc_init();
+ map = map_init();
+
+ if (!config || !map) {
+ perror("malloc");
+ return -1;
+ }
+
+ while ((opt = getopt(argc, argv, "Ff:d:cwlhp:")) != EOF) {
+ switch(opt) {
+ case 'F':
+ printf("Background mode disabled\n");
+ foreground = 1;
+ break;
+ case 'f':
+ printf("Using %s\n", optarg);
+ config_file = optarg;
+ break;
+ case 'p':
+ printf("Using %s\n", optarg);
+ pid_file = optarg;
+ break;
+ case 'd':
+ debug_set = atoi(optarg);
+ break;
+ case 'c':
+ configure = 1;
+ break;
+ case 'w':
+ wait_for_init = 1;
+ break;
+ case 'l':
+ plugin_dump();
+ return 0;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ default:
+ return -1;
+ }
+ }
+
+ if (configure) {
+ return do_configure(config, config_file);
+ }
+
+ if (sc_parse(config, config_file) != 0) {
+ printf("Failed to parse %s\n", config_file);
+ return -1;
+ }
+
+ if (debug_set) {
+ snprintf(val, sizeof(val), "%d", debug_set);
+ sc_set(config, "fence_virtd/@debug", val);
+ } else {
+ if (sc_get(config, "fence_virtd/@debug", val, sizeof(val))==0)
+ debug_set = atoi(val);
+ }
+
+ dset(debug_set);
+
+ if (!foreground) {
+ if (sc_get(config, "fence_virtd/@foreground",
+ val, sizeof(val)) == 0)
+ foreground = atoi(val);
+ }
+
+ if (!wait_for_init) {
+ if (sc_get(config, "fence_virtd/@wait_for_init",
+ val, sizeof(val)) == 0)
+ wait_for_init = atoi(val);
+ if (!wait_for_init) {
+ /* XXX compat */
+ if (sc_get(config, "fence_virtd/@wait_for_backend",
+ val, sizeof(val)) == 0)
+ wait_for_init = atoi(val);
+ }
+ }
+
+ if (dget() > 3)
+ sc_dump(config, stdout);
+
+ if (sc_get(config, "fence_virtd/@backend", backend_name,
+ sizeof(backend_name))) {
+ printf("Failed to determine backend.\n");
+ printf("%s\n", val);
+ return -1;
+ }
+
+ dbg_printf(1, "Backend plugin: %s\n", backend_name);
+
+ if (sc_get(config, "fence_virtd/@listener", listener_name,
+ sizeof(listener_name))) {
+ printf("Failed to determine backend.\n");
+ printf("%s\n", val);
+ return -1;
+ }
+
+ dbg_printf(1, "Listener plugin: %s\n", listener_name);
+
+ if (sc_get(config, "fence_virtd/@module_path", val,
+ sizeof(val))) {
+#ifdef MODULE_PATH
+ snprintf(val, sizeof(val), MODULE_PATH);
+#else
+ printf("Failed to determine module path.\n");
+ return -1;
+#endif
+ }
+
+ dbg_printf(1, "Searching %s for plugins...\n", val);
+
+ opt = plugin_search(val);
+ if (opt > 0) {
+ dbg_printf(1, "%d plugins found\n", opt);
+ } else {
+ printf("No plugins found\n");
+ return 1;
+ }
+
+ if (dget() > 3)
+ plugin_dump();
+
+ lp = plugin_find_listener(listener_name);
+ if (!lp) {
+ printf("Could not find listener \"%s\"\n", listener_name);
+ return 1;
+ }
+
+ p = plugin_find_backend(backend_name);
+ if (!p) {
+ printf("Could not find backend \"%s\"\n", backend_name);
+ return 1;
+ }
+
+ if (pid_file == NULL) {
+ pid_file = malloc(PATH_MAX);
+ memset(pid_file, 0, PATH_MAX);
+ snprintf(pid_file, PATH_MAX, "/var/run/%s.pid", basename(argv[0]));
+ }
+
+ if (check_file_permissions(config_file) != 0)
+ return -1;
+
+ sprintf(val, "listeners/%s/@key_file", listener_name);
+ if (sc_get(config, val,
+ val, sizeof(val)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", val);
+ } else {
+ snprintf(val, sizeof(val), "%s", DEFAULT_KEY_FILE);
+ }
+
+ if (check_file_permissions(val) != 0)
+ return -1;
+
+ openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON);
+
+ daemon_init(basename(argv[0]), pid_file, foreground);
+
+ signal(SIGINT, exit_handler);
+ signal(SIGTERM, exit_handler);
+ signal(SIGQUIT, exit_handler);
+
+ syslog(LOG_NOTICE, "fence_virtd starting. Listener: %s Backend: %s",
+ listener_name, backend_name);
+
+ while (p->init(&backend_ctx, config) < 0) {
+ if (!wait_for_init || !run) {
+ if (foreground) {
+ printf("Backend plugin %s failed to initialize\n",
+ backend_name);
+ }
+ syslog(LOG_ERR,
+ "Backend plugin %s failed to initialize\n",
+ backend_name);
+ return 1;
+ }
+ sleep(5);
+ }
+
+ if (map_load(map, config) < 0) {
+ syslog(LOG_WARNING, "Failed to load static maps\n");
+ }
+
+ /* only client we have now is mcast (fence_xvm behavior) */
+ while (lp->init(&listener_ctx, p->callbacks, config, map,
+ backend_ctx) != 0) {
+ if (!wait_for_init || !run) {
+ if (foreground) {
+ printf("Listener plugin %s failed to initialize\n",
+ listener_name);
+ }
+ syslog(LOG_ERR,
+ "Listener plugin %s failed to initialize\n",
+ listener_name);
+ return 1;
+ }
+ sleep(5);
+ }
+
+ while (run && lp->dispatch(listener_ctx, NULL) >= 0);
+
+ syslog(LOG_NOTICE, "fence_virtd shutting down");
+
+ map_release(map);
+ sc_release(config);
+
+ lp->cleanup(listener_ctx);
+ p->cleanup(backend_ctx);
+
+ plugin_unload();
+ daemon_cleanup();
+
+ return 0;
+}
diff --git a/agents/virt/server/mcast.c b/agents/virt/server/mcast.c
new file mode 100644
index 0000000..a95ab37
--- /dev/null
+++ b/agents/virt/server/mcast.c
@@ -0,0 +1,622 @@
+/*
+ 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 <string.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 <nss.h>
+#include <libgen.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "debug.h"
+#include "fdops.h"
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+
+#define NAME "multicast"
+#define MCAST_VERSION "1.3"
+
+#define MCAST_MAGIC 0xabb911a3
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != MCAST_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+typedef struct _mcast_options {
+ char *addr;
+ char *key_file;
+ int ifindex;
+ int family;
+ unsigned int port;
+ unsigned int hash;
+ unsigned int auth;
+ unsigned int flags;
+} mcast_options;
+
+
+typedef struct _mcast_info {
+ uint64_t magic;
+ void *priv;
+ map_object_t *map;
+ history_info_t *history;
+ char key[MAX_KEY_LEN];
+ mcast_options args;
+ const fence_callbacks_t *cb;
+ ssize_t key_len;
+ int mc_sock;
+ int need_kill;
+} mcast_info;
+
+
+struct mcast_hostlist_arg {
+ map_object_t *map;
+ const char *src;
+ int fd;
+};
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ fence_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+connect_tcp(fence_req_t *req, fence_auth_type_t auth,
+ void *key, size_t key_len)
+{
+ int fd = -1;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ char buf[128];
+
+ switch(req->family) {
+ case PF_INET:
+ memset(&sin, 0, sizeof(sin));
+ memcpy(&sin.sin_addr, req->address,
+ sizeof(sin.sin_addr));
+ sin.sin_family = PF_INET;
+ fd = ipv4_connect(&sin.sin_addr, req->port,
+ 5);
+ if (fd < 0) {
+ printf("Failed to call back\n");
+ return -1;
+ }
+ break;
+ case PF_INET6:
+ memset(&sin6, 0, sizeof(sin6));
+ memcpy(&sin6.sin6_addr, req->address,
+ sizeof(sin6.sin6_addr));
+ sin.sin_family = PF_INET6;
+ fd = ipv6_connect(&sin6.sin6_addr, req->port,
+ 5);
+
+ memset(buf,0,sizeof(buf));
+ inet_ntop(PF_INET6, &sin6.sin6_addr, buf, sizeof(buf));
+
+ if (fd < 0) {
+ printf("Failed to call back %s\n", buf);
+ return -1;
+ }
+ break;
+ default:
+ printf("Family = %d\n", req->family);
+ return -1;
+ }
+
+ /* Noops if auth == AUTH_NONE */
+ if (sock_response(fd, auth, key, key_len, 10) <= 0) {
+ printf("Failed to respond to challenge\n");
+ close(fd);
+ return -1;
+ }
+
+ if (sock_challenge(fd, auth, key, key_len, 10) <= 0) {
+ printf("Remote failed challenge\n");
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+
+static int
+mcast_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct mcast_hostlist_arg *arg = (struct mcast_hostlist_arg *)priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+mcast_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ char val = (char)RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &val, 1, &tv);
+}
+
+
+static int
+mcast_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+do_fence_request_tcp(fence_req_t *req, mcast_info *info)
+{
+ char ip_addr_src[1024];
+ int fd = -1;
+ char response = 1;
+ struct mcast_hostlist_arg arg;
+
+ fd = connect_tcp(req, info->args.auth, info->key, info->key_len);
+ if (fd < 0) {
+ dbg_printf(2, "Could not send reply to fence request: %s\n",
+ strerror(errno));
+ goto out;
+ }
+
+ inet_ntop(req->family, req->address,
+ ip_addr_src, sizeof(ip_addr_src));
+
+ dbg_printf(2, "Request %d seqno %d src %s target %s\n",
+ req->request, req->seqno, ip_addr_src, req->domain);
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->map;
+ arg.src = ip_addr_src;
+ arg.fd = fd;
+
+ mcast_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(mcast_hostlist, &arg,
+ info->priv);
+ mcast_hostlist_end(arg.fd);
+ break;
+ }
+
+ dbg_printf(3, "Sending response to caller...\n");
+ if (_write_retry(fd, &response, 1, NULL) < 0) {
+ perror("write");
+ }
+
+ /* XVM shotguns multicast packets, so we want to avoid
+ * acting on the same request multiple times if the first
+ * attempt was successful.
+ */
+ history_record(info->history, req);
+out:
+ if (fd != -1)
+ close(fd);
+
+ return 1;
+}
+
+
+static int
+mcast_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ mcast_info *info;
+ fence_req_t data;
+ fd_set rfds;
+ struct sockaddr_in sin;
+ int len;
+ int n;
+ socklen_t slen;
+
+ info = (mcast_info *)c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ FD_SET(info->mc_sock, &rfds);
+
+ n = select((info->mc_sock)+1, &rfds, NULL, NULL, timeout);
+ if (n <= 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+ slen = sizeof(sin);
+ len = recvfrom(info->mc_sock, &data, sizeof(data), 0,
+ (struct sockaddr *)&sin, &slen);
+
+ if (len <= 0) {
+ perror("recvfrom");
+ return len;
+ }
+
+ swab_fence_req_t(&data);
+
+ if (!verify_request(&data, info->args.hash, info->key,
+ info->key_len)) {
+ printf("Key mismatch; dropping packet\n");
+ return 0;
+ }
+
+ printf("Request %d seqno %d domain %s\n", data.request, data.seqno,
+ data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ printf("We just did this request; dropping packet\n");
+ return 0;
+ }
+
+ switch(info->args.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ printf("Plain TCP request\n");
+ do_fence_request_tcp(&data, info);
+ break;
+ default:
+ printf("XXX Unhandled authentication\n");
+ }
+
+ return 0;
+}
+
+
+static int
+mcast_config(config_object_t *config, mcast_options *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/multicast/@key_file",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", value);
+ args->key_file = strdup(value);
+ } else {
+ args->key_file = strdup(DEFAULT_KEY_FILE);
+ if (!args->key_file) {
+ dbg_printf(1, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ args->hash = DEFAULT_HASH;
+ if (sc_get(config, "listeners/multicast/@hash",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for hash\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = HASH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = HASH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = HASH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = HASH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported hash: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->auth = DEFAULT_AUTH;
+ if (sc_get(config, "listeners/multicast/@auth",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for auth\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->auth = AUTH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->auth = AUTH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->auth = AUTH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->auth = AUTH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported auth: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->family = PF_INET;
+ if (sc_get(config, "listeners/multicast/@family",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for family\n", value);
+ if (!strcasecmp(value, "ipv4")) {
+ args->family = PF_INET;
+ } else if (!strcasecmp(value, "ipv6")) {
+ args->family = PF_INET6;
+ } else {
+ dbg_printf(1, "Unsupported family: %s\n", value);
+ ++errors;
+ }
+ }
+
+ if (sc_get(config, "listeners/multicast/@address",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for address\n", value);
+ args->addr = strdup(value);
+ } else {
+ if (args->family == PF_INET) {
+ args->addr = strdup(IPV4_MCAST_DEFAULT);
+ } else {
+ args->addr = strdup(IPV6_MCAST_DEFAULT);
+ }
+ }
+ if (!args->addr) {
+ return -1;
+ }
+
+ args->port = DEFAULT_MCAST_PORT;
+ if (sc_get(config, "listeners/multicast/@port",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for port\n", value);
+ args->port = atoi(value);
+ if (args->port <= 0) {
+ dbg_printf(1, "Invalid port: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->ifindex = 0;
+ if (sc_get(config, "listeners/multicast/@interface",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for interface\n", value);
+ args->ifindex = if_nametoindex(value);
+ if (args->ifindex < 0) {
+ dbg_printf(1, "Invalid interface: %s\n", value);
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+
+static int
+mcast_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ mcast_info *info;
+ int mc_sock, ret;
+
+ /* 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;
+ }
+
+ info = malloc(sizeof(*info));
+ if (!info)
+ return -1;
+ memset(info, 0, sizeof(*info));
+
+ info->priv = priv;
+ info->cb = cb;
+ info->map = map;
+
+ ret = mcast_config(config, &info->args);
+ if (ret < 0) {
+ perror("mcast_config");
+ free(info);
+ return -1;
+ } else if (ret > 0) {
+ printf("%d errors found during configuration\n",ret);
+ free(info);
+ return -1;
+ }
+
+ if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
+ info->key_len = read_key_file(info->args.key_file,
+ info->key, sizeof(info->key));
+ if (info->key_len < 0) {
+ printf("Could not read %s; operating without "
+ "authentication\n", info->args.key_file);
+ info->args.auth = AUTH_NONE;
+ info->args.hash = HASH_NONE;
+ info->key_len = 0;
+ }
+ }
+
+ if (info->args.family == PF_INET)
+ mc_sock = ipv4_recv_sk(info->args.addr,
+ info->args.port,
+ info->args.ifindex);
+ else
+ mc_sock = ipv6_recv_sk(info->args.addr,
+ info->args.port,
+ info->args.ifindex);
+ if (mc_sock < 0) {
+ printf("Could not set up multicast listen socket\n");
+ free(info);
+ return -1;
+ }
+
+ info->magic = MCAST_MAGIC;
+ info->mc_sock = mc_sock;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ return 0;
+}
+
+
+static int
+mcast_shutdown(listener_context_t c)
+{
+ mcast_info *info = (mcast_info *)c;
+
+ VALIDATE(info);
+ info->magic = 0;
+ history_wipe(info->history);
+ free(info->history);
+ free(info->args.key_file);
+ free(info->args.addr);
+ close(info->mc_sock);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t mcast_plugin = {
+ .name = NAME,
+ .version = MCAST_VERSION,
+ .init = mcast_init,
+ .dispatch = mcast_dispatch,
+ .cleanup = mcast_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &mcast_plugin;
+}
diff --git a/agents/virt/server/plugin.c b/agents/virt/server/plugin.c
new file mode 100644
index 0000000..d9a69fb
--- /dev/null
+++ b/agents/virt/server/plugin.c
@@ -0,0 +1,417 @@
+/*
+ Copyright Red Hat, Inc. 2002-2004, 2009
+
+ The Magma Cluster API Library is free software; you can redistribute
+ it and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either version
+ 2.1 of the License, or (at your option) any later version.
+
+ The Magma Cluster API Library is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+ */
+/** @file
+ * Plugin loading routines
+ */
+
+#include "config.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <string.h>
+#include <dirent.h>
+
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "debug.h"
+
+typedef struct _plugin_list {
+ list_head();
+ const listener_plugin_t *listener;
+ const backend_plugin_t *backend;
+ void *handle;
+ plugin_type_t type;
+} plugin_list_t;
+
+static plugin_list_t *server_plugins = NULL;
+
+
+static int
+plugin_reg_backend(void *handle, const backend_plugin_t *plugin)
+{
+ plugin_list_t *newplug;
+
+ if (plugin_find_backend(plugin->name)) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ newplug = malloc(sizeof(*newplug));
+ if (!newplug)
+ return -1;
+ memset(newplug, 0, sizeof(*newplug));
+ newplug->backend = plugin;
+ newplug->type = PLUGIN_BACKEND;
+ newplug->handle = handle;
+
+ list_insert(&server_plugins, newplug);
+ return 0;
+}
+
+
+static int
+plugin_reg_listener(void *handle, const listener_plugin_t *plugin)
+{
+ plugin_list_t *newplug;
+
+ if (plugin_find_listener(plugin->name)) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ newplug = malloc(sizeof(*newplug));
+ if (!newplug)
+ return -1;
+ memset(newplug, 0, sizeof(*newplug));
+ newplug->listener = plugin;
+ newplug->type = PLUGIN_LISTENER;
+ newplug->handle = handle;
+
+ list_insert(&server_plugins, newplug);
+ return 0;
+}
+
+
+void
+plugin_dump(void)
+{
+ plugin_list_t *p;
+ int x, y;
+
+ y = 0;
+ list_for(&server_plugins, p, x) {
+ if (p->type == PLUGIN_BACKEND) {
+ if (!y) {
+ y = 1;
+ printf("Available backends:\n");
+ }
+ printf(" %s %s\n",
+ p->backend->name, p->backend->version);
+ }
+ }
+
+ y = 0;
+ list_for(&server_plugins, p, x) {
+ if (p->type == PLUGIN_LISTENER) {
+ if (!y) {
+ y = 1;
+ printf("Available listeners:\n");
+ }
+ printf(" %s %s\n",
+ p->listener->name, p->listener->version);
+ }
+ }
+}
+
+
+const backend_plugin_t *
+plugin_find_backend(const char *name)
+{
+ plugin_list_t *p;
+ int x;
+
+ list_for(&server_plugins, p, x) {
+ if (p->type != PLUGIN_BACKEND)
+ continue;
+ if (!strcasecmp(name, p->backend->name))
+ return p->backend;
+ }
+
+ return NULL;
+}
+
+
+const listener_plugin_t *
+plugin_find_listener(const char *name)
+{
+ plugin_list_t *p;
+ int x;
+
+ list_for(&server_plugins, p, x) {
+ if (p->type != PLUGIN_LISTENER)
+ continue;
+ if (!strcasecmp(name, p->listener->name))
+ return p->listener;
+ }
+
+ return NULL;
+}
+
+
+static int
+backend_plugin_load(void *handle, const char *libpath)
+{
+ const backend_plugin_t *plug = NULL;
+ double (*modversion)(void);
+ backend_plugin_t *(*modinfo)(void);
+
+ modversion = dlsym(handle, BACKEND_VER_STR);
+ if (!modversion) {
+ dbg_printf(1, "Failed to map %s\n", BACKEND_VER_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (modversion() != PLUGIN_VERSION_BACKEND) {
+ dbg_printf(1, "API version mismatch in %s: \n"
+ " %f expected; %f received.\n", libpath,
+ PLUGIN_VERSION_BACKEND, modversion());
+ errno = EINVAL;
+ return -1;
+ }
+
+ modinfo = dlsym(handle, BACKEND_INFO_STR);
+ if (!modinfo) {
+ dbg_printf(1, "Failed to map %s\n", BACKEND_INFO_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ plug = modinfo();
+ if (plugin_reg_backend(handle, plug) < 0) {
+ dbg_printf(1, "Failed to register %s %s\n", plug->name,
+ plug->version);
+ errno = EINVAL;
+ return -1;
+ } else {
+ dbg_printf(1, "Registered backend plugin %s %s\n",
+ plug->name, plug->version);
+ }
+
+ return 0;
+}
+
+
+static int
+listener_plugin_load(void *handle, const char *libpath)
+{
+ const listener_plugin_t *plug = NULL;
+ double (*modversion)(void);
+ listener_plugin_t *(*modinfo)(void);
+
+ modversion = dlsym(handle, LISTENER_VER_STR);
+ if (!modversion) {
+ dbg_printf(1, "Failed to map %s\n", LISTENER_VER_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (modversion() != PLUGIN_VERSION_LISTENER) {
+ dbg_printf(1, "API version mismatch in %s: \n"
+ " %f expected; %f received.\n", libpath,
+ PLUGIN_VERSION_LISTENER, modversion());
+ dlclose(handle);
+ errno = EINVAL;
+ return -1;
+ }
+
+ modinfo = dlsym(handle, LISTENER_INFO_STR);
+ if (!modinfo) {
+ dbg_printf(1, "Failed to map %s\n", LISTENER_INFO_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ plug = modinfo();
+ if (plugin_reg_listener(handle, plug) < 0) {
+ dbg_printf(1, "Failed to register %s %s\n", plug->name,
+ plug->version);
+ errno = EINVAL;
+ return -1;
+ } else {
+ dbg_printf(1, "Registered listener plugin %s %s\n",
+ plug->name, plug->version);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Load a cluster plugin .so file and map all the functions
+ * provided to entries in a backend_plugin_t structure.
+ *
+ * @param libpath Path to file.
+ * @return NULL on failure, or plugin-specific
+ * (const) backend_plugin_t * structure on
+ * success.
+ */
+int
+plugin_load(const char *libpath)
+{
+ void *handle = NULL;
+
+ errno = 0;
+
+ if (!libpath) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ dbg_printf(3, "Loading plugin from %s\n", libpath);
+ handle = dlopen(libpath, RTLD_NOW);
+ if (!handle) {
+ dbg_printf(3, "Could not dlopen %s: %s\n", libpath, dlerror());
+ errno = ELIBACC;
+ return -1;
+ }
+
+ if (!backend_plugin_load(handle, libpath) ||
+ !listener_plugin_load(handle, libpath))
+ return 0;
+
+ dbg_printf(3, "%s is not a valid plugin\n", libpath);
+ dlclose(handle);
+ errno = EINVAL;
+ return -1;
+}
+
+void
+plugin_unload(void)
+{
+ plugin_list_t *p;
+ int x;
+
+ list_for(&server_plugins, p, x) {
+ dlclose(p->handle);
+ }
+}
+
+/**
+ Free up a null-terminated array of strings
+ */
+static void
+free_dirnames(char **dirnames)
+{
+ int x = 0;
+
+ for (; dirnames[x]; x++)
+ free(dirnames[x]);
+
+ free(dirnames);
+}
+
+
+static int
+_compare(const void *a, const void *b)
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+
+/**
+ Read all entries in a directory and return them in a NULL-terminated,
+ sorted array.
+ */
+static int
+read_dirnames_sorted(const char *directory, char ***dirnames)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char filename[1024];
+ int count = 0, x = 0;
+
+ dir = opendir(directory);
+ if (!dir)
+ return -1;
+
+ /* Count the number of plugins */
+ while ((entry = readdir(dir)) != NULL)
+ ++count;
+
+ /* Malloc the entries */
+ *dirnames = malloc(sizeof(char *) * (count+1));
+ if (!*dirnames) {
+#ifdef DEBUG
+ printf("%s: Failed to malloc %d bytes",
+ __FUNCTION__, (int)(sizeof(char *) * (count+1)));
+#endif
+ closedir(dir);
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(*dirnames, 0, sizeof(char *) * (count + 1));
+ rewinddir(dir);
+
+ /* Store the directory names. */
+ while ((entry = readdir(dir)) != NULL) {
+ snprintf(filename, sizeof(filename), "%s/%s", directory,
+ entry->d_name);
+
+ (*dirnames)[x] = strdup(filename);
+ if (!(*dirnames)[x]) {
+#ifdef DEBUG
+ printf("Failed to duplicate %s\n",
+ filename);
+#endif
+ free_dirnames(*dirnames);
+ closedir(dir);
+ errno = ENOMEM;
+ return -1;
+ }
+ ++x;
+ }
+
+ closedir(dir);
+
+ /* Sort the directory names. */
+ qsort((*dirnames), count, sizeof(char *), _compare);
+
+ return 0;
+}
+
+
+/**
+ */
+int
+plugin_search(const char *pathname)
+{
+ int found = 0;
+ int fcount = 0;
+ char **filenames;
+
+ dbg_printf(1, "Searching for plugins in %s\n", pathname);
+ if (read_dirnames_sorted(pathname, &filenames) != 0) {
+ return -1;
+ }
+
+ for (fcount = 0; filenames[fcount]; fcount++) {
+
+ if (plugin_load(filenames[fcount]) == 0)
+ ++found;
+ }
+
+ free_dirnames(filenames);
+ if (!found) {
+ dbg_printf(1, "No usable plugins found.\n");
+ errno = ELIBACC;
+ return -1;
+ }
+
+ return found;
+}
diff --git a/agents/virt/server/serial.c b/agents/virt/server/serial.c
new file mode 100644
index 0000000..bde8218
--- /dev/null
+++ b/agents/virt/server/serial.c
@@ -0,0 +1,459 @@
+/*
+ Copyright Red Hat, Inc. 2010
+
+ 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 <string.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 <nss.h>
+#include <libgen.h>
+
+/* Local includes */
+#include "debug.h"
+#include "fdops.h"
+#include "serial.h"
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+#include "xvm.h"
+
+#define NAME "serial"
+#define SERIAL_VERSION "0.5"
+
+#define SERIAL_PLUG_MAGIC 0x1227a000
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != SERIAL_PLUG_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+
+typedef struct _serial_info {
+ uint64_t magic;
+ const fence_callbacks_t *cb;
+ void *priv;
+ char *uri;
+ char *path;
+ history_info_t *history;
+ map_object_t *maps;
+ int mode;
+ int wake_fd;
+} serial_info;
+
+
+struct serial_hostlist_arg {
+ map_object_t *map;
+ const char *src;
+ int fd;
+};
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ serial_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+serial_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct serial_hostlist_arg *arg = (struct serial_hostlist_arg *)priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+serial_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ serial_resp_t resp;
+
+ resp.magic = SERIAL_MAGIC;
+ resp.response = RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &resp, sizeof(resp), &tv);
+}
+
+
+static int
+serial_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ //printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+do_fence_request(int fd, const char *src, serial_req_t *req, serial_info *info)
+{
+ char response = RESP_FAIL;
+ struct serial_hostlist_arg arg;
+ serial_resp_t resp;
+
+ arg.fd = fd;
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->maps;
+ arg.src = src;
+ arg.fd = fd;
+
+ serial_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(serial_hostlist, &arg,
+ info->priv);
+ serial_hostlist_end(arg.fd);
+ break;
+ }
+
+ resp.magic = SERIAL_MAGIC;
+ resp.response = response;
+ swab_serial_resp_t(&resp);
+
+ dbg_printf(3, "Sending response to caller...\n");
+ if (_write_retry(fd, &resp, sizeof(resp), NULL) < 0)
+ perror("write");
+
+ /* XVM shotguns multicast packets, so we want to avoid
+ * acting on the same request multiple times if the first
+ * attempt was successful.
+ */
+ history_record(info->history, req);
+
+ return 1;
+}
+
+
+static int
+serial_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ char src_domain[MAX_DOMAINNAME_LENGTH];
+ serial_info *info;
+ serial_req_t data;
+ fd_set rfds;
+ struct timeval tv;
+ int max;
+ int n, x, ret;
+
+ info = (serial_info *)c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ domain_sock_fdset(&rfds, &max);
+ FD_SET(info->wake_fd, &rfds);
+ if (info->wake_fd > max)
+ max = info->wake_fd;
+
+ n = select(max+1, &rfds, NULL, NULL, timeout);
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+ /*
+ * See if the goal was just to be woken up in order to refill our
+ * file descriptor set. For example, if multiple domains were
+ * created simultaneously, we would have to refill our fd_set
+ */
+ if (FD_ISSET(info->wake_fd, &rfds)) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ _read_retry(info->wake_fd, &c, 1, &tv);
+ return 0;
+ }
+
+ /*
+ * If no requests, we're done
+ */
+ if (n == 0)
+ return 0;
+
+ /* find & read request */
+ for (x = 0; x <= max; x++) {
+ if (FD_ISSET(x, &rfds)) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ ret = _read_retry(x, &data, sizeof(data), &tv);
+
+ if (ret != sizeof(data)) {
+ if (--n > 0)
+ continue;
+ else
+ return 0;
+ } else {
+ swab_serial_req_t(&data);
+ break;
+ }
+ }
+ }
+
+ src_domain[0] = 0;
+ domain_sock_name(x, src_domain, sizeof(src_domain));
+
+ dbg_printf(2, "Sock %d Request %d seqno %d src %s target %s\n", x,
+ data.request, data.seqno, src_domain, data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ dbg_printf(3, "We just did this request; dropping packet\n");
+ return 0;
+ }
+
+ do_fence_request(x, src_domain[0] == 0 ? NULL : src_domain,
+ &data, info);
+
+ return 0;
+}
+
+
+static int
+serial_config(config_object_t *config, serial_info *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/serial/@uri",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for uri\n", value);
+ args->uri = strdup(value);
+ }
+
+ if (sc_get(config, "listeners/serial/@path",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for path\n", value);
+ args->path = strdup(value);
+ }
+
+ if (sc_get(config, "listeners/serial/@mode",
+ value, sizeof(value)-1) == 0) {
+ if (!strcasecmp(value, "vmchannel")) {
+ args->mode = 1;
+ } else if (!strcasecmp(value, "serial")) {
+ args->mode = 0;
+ } else {
+ args->mode = atoi(value);
+ if (args->mode < 0)
+ args->mode = 0;
+ }
+
+ dbg_printf(1, "Got %s for mode\n",
+ args->mode?"VMChannel":"serial");
+
+ }
+
+ return errors;
+}
+
+
+static int
+serial_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ serial_info *info;
+ int ret;
+
+ info = malloc(sizeof(*info));
+ if (!info)
+ return -1;
+ memset(info, 0, sizeof(*info));
+
+ info->priv = priv;
+ info->cb = cb;
+
+ ret = serial_config(config, info);
+ if (ret < 0) {
+ perror("serial_config");
+ return -1;
+ } else if (ret > 0) {
+ printf("%d errors found during configuration\n",ret);
+ return -1;
+ }
+
+ info->maps = map;
+
+ info->magic = SERIAL_PLUG_MAGIC;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ start_event_listener(info->uri, info->path, info->mode, &info->wake_fd);
+ sleep(1);
+
+ return 0;
+}
+
+
+static int
+serial_shutdown(listener_context_t c)
+{
+ serial_info *info = (serial_info *)c;
+
+ dbg_printf(3, "Shutting down serial\n");
+
+ VALIDATE(info);
+ info->magic = 0;
+ stop_event_listener();
+ domain_sock_cleanup();
+ history_wipe(info->history);
+ free(info->history);
+ free(info->uri);
+ free(info->path);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t serial_plugin = {
+ .name = NAME,
+ .version = SERIAL_VERSION,
+ .init = serial_init,
+ .dispatch = serial_dispatch,
+ .cleanup = serial_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &serial_plugin;
+}
diff --git a/agents/virt/server/serial.h b/agents/virt/server/serial.h
new file mode 100644
index 0000000..481400a
--- /dev/null
+++ b/agents/virt/server/serial.h
@@ -0,0 +1,20 @@
+#ifndef __VIRT_SERIAL_H
+#define __VIRT_SERIAL_H
+
+#include <sys/select.h>
+
+/* virt-sockets.c */
+int domain_sock_setup(const char *domain, const char *socket_path);
+int domain_sock_close(const char *domain);
+int domain_sock_fdset(fd_set *set, int *max);
+
+/* Find the domain name associated with a FD */
+int domain_sock_name(int fd, char *outbuf, size_t buflen);
+int domain_sock_cleanup(void);
+
+/* virt-serial.c - event thread control functions */
+int start_event_listener(const char *uri, const char *path, int mode, int *wake_fd);
+int stop_event_listener(void);
+
+
+#endif
diff --git a/agents/virt/server/static_map.c b/agents/virt/server/static_map.c
new file mode 100644
index 0000000..7bdc400
--- /dev/null
+++ b/agents/virt/server/static_map.c
@@ -0,0 +1,237 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "list.h"
+#include "debug.h"
+#include "serial.h"
+#include "uuid-test.h"
+
+struct perm_entry {
+ list_head();
+ char name[129];
+};
+
+struct perm_group {
+ list_head();
+ struct perm_entry *uuids;
+ struct perm_entry *ips;
+ char name[129];
+};
+
+
+static void
+static_map_cleanup(void **info)
+{
+ struct perm_group *groups = (struct perm_group *)(*info);
+ struct perm_group *group;
+ struct perm_entry *entry;
+
+ while (groups) {
+ group = groups;
+ list_remove(&groups, group);
+ while (group->uuids) {
+ entry = group->uuids;
+ list_remove(&group->uuids, entry);
+ free(entry);
+ }
+ while (group->ips) {
+ entry = group->ips;
+ list_remove(&group->ips, entry);
+ free(entry);
+ }
+ free(group);
+ }
+
+ *info = NULL;
+}
+
+
+static int
+static_map_check(void *info, const char *src, const char *tgt_uuid, const char *tgt_name)
+{
+ struct perm_group *groups = (struct perm_group *)info;
+ struct perm_group *group;
+ struct perm_entry *left, *tmp;
+ int x, y, uuid = 0;
+
+ if (!info)
+ return 1; /* no maps == wide open */
+
+ dbg_printf(99, "[server:map_check] map request: src: %s uuid: %s name: %s\n", src, tgt_uuid, tgt_name);
+
+ uuid = is_uuid(src);
+
+ list_for(&groups, group, x) {
+ left = NULL;
+
+ if (uuid) {
+ list_for(&group->uuids, tmp, y) {
+ if (!strcasecmp(tmp->name, src)) {
+ left = tmp;
+ break;
+ }
+ }
+ } else {
+ list_for(&group->ips, tmp, y) {
+ if (!strcasecmp(tmp->name, src)) {
+ left = tmp;
+ break;
+ }
+ }
+ }
+
+ if (!left)
+ continue;
+
+ list_for(&group->uuids, tmp, y) {
+ if (!strcasecmp(tmp->name, tgt_uuid)) {
+ return 1;
+ }
+ /* useful only for list */
+ if (tgt_name) {
+ if (!strcasecmp(tmp->name, tgt_name)) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+static_map_load(void *config_ptr, void **perm_info)
+{
+ config_object_t *config = config_ptr;
+ int group_idx = 0;
+ int entry_idx = 0;
+ int found;
+ char value[128];
+ char buf[256];
+ char buf2[512];
+ struct perm_group *group = NULL, *groups = NULL;
+ struct perm_entry *entry = NULL;
+
+ if (!perm_info)
+ return -1;
+
+ do {
+ snprintf(buf, sizeof(buf)-1, "groups/group[%d]", ++group_idx);
+
+ if (sc_get(config, buf, value, sizeof(value)) != 0) {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@uuid", buf);
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@ip", buf);
+ if (sc_get(config, buf2, value,
+ sizeof(value)) != 0) {
+ break;
+ }
+ }
+ snprintf(buf2, sizeof(buf2)-1, "%s/@name", buf);
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ snprintf(value, sizeof(value), "unnamed-%d",
+ group_idx);
+ }
+ }
+
+ group = malloc(sizeof(*group));
+ assert(group);
+ memset(group, 0, sizeof(*group));
+ strncpy(group->name, value, sizeof(group->name));
+ dbg_printf(3, "Group: %s\n", value);
+
+ found = 0;
+ entry_idx = 0;
+ do {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@uuid[%d]",
+ buf, ++entry_idx);
+
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ break;
+ }
+
+ ++found;
+ entry = malloc(sizeof(*entry));
+ assert(entry);
+ memset(entry, 0, sizeof(*entry));
+ strncpy(entry->name, value, sizeof(entry->name));
+ dbg_printf(3, " - UUID Entry: %s\n", value);
+
+ list_insert(&group->uuids, entry);
+
+ } while (1);
+
+ entry_idx = 0;
+ do {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@ip[%d]",
+ buf, ++entry_idx);
+
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ break;
+ }
+
+ ++found;
+ entry = malloc(sizeof(*entry));
+ assert(entry);
+ memset(entry, 0, sizeof(*entry));
+ strncpy(entry->name, value, sizeof(entry->name));
+ dbg_printf(3, " - IP Entry: %s\n", value);
+
+ list_insert(&group->ips, entry);
+
+ } while (1);
+
+
+ if (!found)
+ free(group);
+ else
+ list_insert(&groups, group);
+
+ } while (1);
+
+ *perm_info = groups;
+
+ return 0;
+}
+
+
+static const map_object_t static_map_obj = {
+ .load = static_map_load,
+ .check = static_map_check,
+ .cleanup = static_map_cleanup,
+ .info = NULL
+};
+
+
+void *
+map_init(void)
+{
+ map_object_t *o;
+
+ o = malloc(sizeof(*o));
+ if (!o)
+ return NULL;
+ memset(o, 0, sizeof(*o));
+ memcpy(o, &static_map_obj, sizeof(*o));
+
+ return (void *)o;
+}
+
+
+void
+map_release(void *c)
+{
+ map_object_t *o = (map_object_t *)c;
+
+ static_map_cleanup(&o->info);
+ free(c);
+}
diff --git a/agents/virt/server/tcp.c b/agents/virt/server/tcp.c
new file mode 100644
index 0000000..c1fb60c
--- /dev/null
+++ b/agents/virt/server/tcp.c
@@ -0,0 +1,575 @@
+/*
+ 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 <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <nss.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "tcp_listener.h"
+#include "debug.h"
+#include "fdops.h"
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+
+#define NAME "tcp"
+#define TCP_VERSION "0.2"
+
+#define TCP_MAGIC 0xc3dff7a9
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != TCP_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+typedef struct _tcp_options {
+ char *key_file;
+ char *addr;
+ int family;
+ unsigned int port;
+ unsigned int hash;
+ unsigned int auth;
+ unsigned int flags;
+} tcp_options;
+
+
+typedef struct _tcp_info {
+ uint64_t magic;
+ void *priv;
+ map_object_t *map;
+ history_info_t *history;
+ char key[MAX_KEY_LEN];
+ tcp_options args;
+ const fence_callbacks_t *cb;
+ ssize_t key_len;
+ int listen_sock;
+} tcp_info;
+
+
+struct tcp_hostlist_arg {
+ map_object_t *map;
+ const char *src;
+ int fd;
+};
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ fence_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+tcp_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct tcp_hostlist_arg *arg = (struct tcp_hostlist_arg *)priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+tcp_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ char val = (char)RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &val, 1, &tv);
+}
+
+
+static int
+tcp_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+static socklen_t
+sockaddr_len(const struct sockaddr_storage *ss)
+{
+ if (ss->ss_family == AF_INET) {
+ return sizeof(struct sockaddr_in);
+ } else {
+ return sizeof(struct sockaddr_in6);
+ }
+}
+
+static int
+do_fence_request_tcp(int fd, struct sockaddr_storage *ss, socklen_t sock_len, fence_req_t *req, tcp_info *info)
+{
+ char ip_addr_src[1024];
+ char response = 1;
+ struct tcp_hostlist_arg arg;
+ int ret;
+
+ /* Noops if auth == AUTH_NONE */
+ if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) {
+ printf("Failed to respond to challenge\n");
+ close(fd);
+ return -1;
+ }
+
+ ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10);
+ if (ret <= 0) {
+ printf("Remote failed challenge\n");
+ close(fd);
+ return -1;
+ }
+
+
+ if (getnameinfo((struct sockaddr *)ss, sockaddr_len(ss),
+ ip_addr_src, sizeof(ip_addr_src),
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ printf("Unable to resolve!\n");
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(2, "Request %d seqno %d src %s target %s\n",
+ req->request, req->seqno, ip_addr_src, req->domain);
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->map;
+ arg.src = ip_addr_src;
+ arg.fd = fd;
+
+ tcp_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(tcp_hostlist, &arg,
+ info->priv);
+ tcp_hostlist_end(arg.fd);
+ break;
+ }
+
+ dbg_printf(3, "Sending response to caller...\n");
+ if (_write_retry(fd, &response, 1, NULL) < 0) {
+ perror("write");
+ }
+
+ history_record(info->history, req);
+
+ if (fd != -1)
+ close(fd);
+
+ return 1;
+}
+
+
+static int
+tcp_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ tcp_info *info;
+ fence_req_t data;
+ fd_set rfds;
+ int n;
+ int client_fd;
+ int ret;
+ struct timeval tv;
+ struct sockaddr_storage ss;
+ socklen_t sock_len = sizeof(ss);
+
+ if (timeout != NULL)
+ memcpy(&tv, timeout, sizeof(tv));
+ else {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ }
+
+ info = (tcp_info *)c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ FD_SET(info->listen_sock, &rfds);
+
+ n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout);
+ if (n <= 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+ client_fd = accept(info->listen_sock, (struct sockaddr *)&ss, &sock_len);
+ if (client_fd < 0) {
+ perror("accept");
+ return -1;
+ }
+
+ dbg_printf(3, "Accepted client...\n");
+
+ ret = _read_retry(client_fd, &data, sizeof(data), &tv);
+ if (ret != sizeof(data)) {
+ dbg_printf(3, "Invalid request (read %d bytes)\n", ret);
+ close(client_fd);
+ return 0;
+ }
+
+ swab_fence_req_t(&data);
+
+ if (!verify_request(&data, info->args.hash, info->key,
+ info->key_len)) {
+ printf("Key mismatch; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ dbg_printf(3, "Request %d seqno %d domain %s\n",
+ data.request, data.seqno, data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ printf("We just did this request; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ switch(info->args.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ printf("Plain TCP request\n");
+ do_fence_request_tcp(client_fd, &ss, sock_len, &data, info);
+ break;
+ default:
+ printf("XXX Unhandled authentication\n");
+ }
+
+ return 0;
+}
+
+
+static int
+tcp_config(config_object_t *config, tcp_options *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/tcp/@key_file",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", value);
+ args->key_file = strdup(value);
+ } else {
+ args->key_file = strdup(DEFAULT_KEY_FILE);
+ if (!args->key_file) {
+ dbg_printf(1, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ args->hash = DEFAULT_HASH;
+ if (sc_get(config, "listeners/tcp/@hash",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for hash\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = HASH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = HASH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = HASH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = HASH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported hash: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->auth = DEFAULT_AUTH;
+ if (sc_get(config, "listeners/tcp/@auth",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for auth\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = AUTH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = AUTH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = AUTH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = AUTH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported auth: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->family = PF_INET;
+ if (sc_get(config, "listeners/tcp/@family",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for family\n", value);
+ if (!strcasecmp(value, "ipv4")) {
+ args->family = PF_INET;
+ } else if (!strcasecmp(value, "ipv6")) {
+ args->family = PF_INET6;
+ } else {
+ dbg_printf(1, "Unsupported family: %s\n", value);
+ ++errors;
+ }
+ }
+
+ if (sc_get(config, "listeners/tcp/@address",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for address\n", value);
+ args->addr = strdup(value);
+ } else {
+ if (args->family == PF_INET) {
+ args->addr = strdup(IPV4_TCP_ADDR_DEFAULT);
+ } else {
+ args->addr = strdup(IPV6_TCP_ADDR_DEFAULT);
+ }
+ }
+ if (!args->addr) {
+ return -1;
+ }
+
+ args->port = DEFAULT_MCAST_PORT;
+ if (sc_get(config, "listeners/tcp/@port",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for port\n", value);
+ args->port = atoi(value);
+ if (args->port <= 0) {
+ dbg_printf(1, "Invalid port: %s\n", value);
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+
+static int
+tcp_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ tcp_info *info;
+ int listen_sock, ret;
+
+ /* 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;
+ }
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+
+ info->priv = priv;
+ info->cb = cb;
+ info->map = map;
+
+ ret = tcp_config(config, &info->args);
+ if (ret < 0)
+ perror("tcp_config");
+ else if (ret > 0)
+ printf("%d errors found during configuration\n",ret);
+
+ if (ret != 0) {
+ if (info->args.key_file)
+ free(info->args.key_file);
+ if (info->args.addr)
+ free(info->args.addr);
+ free(info);
+ return -1;
+ }
+
+ if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
+ info->key_len = read_key_file(info->args.key_file,
+ info->key, sizeof(info->key));
+ if (info->key_len < 0) {
+ printf("Could not read %s; operating without "
+ "authentication\n", info->args.key_file);
+ info->args.auth = AUTH_NONE;
+ info->args.hash = HASH_NONE;
+ info->key_len = 0;
+ }
+ }
+
+ if (info->args.family == PF_INET) {
+ listen_sock = ipv4_listen(info->args.addr, info->args.port, 10);
+ } else {
+ listen_sock = ipv6_listen(info->args.addr, info->args.port, 10);
+ }
+
+ if (listen_sock < 0) {
+ printf("Could not set up listen socket\n");
+ if (info->args.key_file)
+ free(info->args.key_file);
+ if (info->args.addr)
+ free(info->args.addr);
+ free(info);
+ return -1;
+ }
+
+ info->magic = TCP_MAGIC;
+ info->listen_sock = listen_sock;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ return 0;
+}
+
+
+static int
+tcp_shutdown(listener_context_t c)
+{
+ tcp_info *info = (tcp_info *)c;
+
+ VALIDATE(info);
+ info->magic = 0;
+ history_wipe(info->history);
+ free(info->history);
+ free(info->args.key_file);
+ free(info->args.addr);
+ close(info->listen_sock);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t tcp_plugin = {
+ .name = NAME,
+ .version = TCP_VERSION,
+ .init = tcp_init,
+ .dispatch = tcp_dispatch,
+ .cleanup = tcp_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &tcp_plugin;
+}
diff --git a/agents/virt/server/uuid-test.c b/agents/virt/server/uuid-test.c
new file mode 100644
index 0000000..3116ef9
--- /dev/null
+++ b/agents/virt/server/uuid-test.c
@@ -0,0 +1,66 @@
+#include "config.h"
+
+#include <uuid/uuid.h>
+#include <errno.h>
+#include <string.h>
+
+#include "uuid-test.h"
+
+int
+is_uuid(const char *value)
+{
+ uuid_t id;
+ char test_value[37];
+
+ if (strlen(value) < 36) {
+ return 0;
+ }
+
+ memset(id, 0, sizeof(uuid_t));
+
+ if (uuid_is_null(id) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (uuid_parse(value, id) < 0) {
+ return 0;
+ }
+
+ memset(test_value, 0, sizeof(test_value));
+ uuid_unparse(id, test_value);
+
+ if (strcasecmp(value, test_value)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef STANDALONE
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+ int ret;
+
+ if (argc < 2) {
+ printf("Usage: uuidtest <value>\n");
+ return 1;
+ }
+
+ ret = is_uuid(argv[1]);
+ if (ret == 0) {
+ printf("%s is NOT a uuid\n", argv[1]);
+ } else if (ret == 1) {
+ printf("%s is a uuid\n", argv[1]);
+ } else {
+ printf("Error: %s\n", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/agents/virt/server/uuid-test.h b/agents/virt/server/uuid-test.h
new file mode 100644
index 0000000..164fec7
--- /dev/null
+++ b/agents/virt/server/uuid-test.h
@@ -0,0 +1,14 @@
+#ifndef __UUID_TEST_H
+#define __UUID_TEST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int is_uuid(const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/agents/virt/server/virt-serial.c b/agents/virt/server/virt-serial.c
new file mode 100644
index 0000000..6b369bc
--- /dev/null
+++ b/agents/virt/server/virt-serial.c
@@ -0,0 +1,444 @@
+// #include <config.h>
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <libvirt/libvirt.h>
+
+#include <libxml/xmlreader.h>
+
+#include "simpleconfig.h"
+#include "debug.h"
+
+#define DEBUG0(fmt) dbg_printf(5,"%s:%d :: " fmt "\n", \
+ __func__, __LINE__)
+#define DEBUG1(fmt, ...) dbg_printf(5, "%s:%d: " fmt "\n", \
+ __func__, __LINE__, __VA_ARGS__)
+
+#include "serial.h"
+
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+
+static pthread_t event_tid = 0;
+static int run = 0;
+
+/* Prototypes */
+const char *eventToString(int event);
+int myDomainEventCallback1(virConnectPtr conn, virDomainPtr dom,
+ int event, int detail, void *opaque);
+
+void usage(const char *pname);
+
+struct domain_info {
+ virDomainPtr dom;
+ virDomainEventType event;
+};
+
+static int
+is_in_directory(const char *dir, const char *pathspec)
+{
+ char *last_slash = NULL;
+ size_t dirlen, pathlen;
+
+ if (!dir || !pathspec)
+ return 0;
+
+ dirlen = strlen(dir);
+ pathlen = strlen(pathspec);
+
+ /*
+ printf("dirlen = %d pathlen = %d\n",
+ dirlen, pathlen);
+ */
+
+ /* chop off trailing slashes */
+ while (dirlen && dir[dirlen-1]=='/')
+ --dirlen;
+
+ /* chop off leading slashes */
+ while (dirlen && dir[0] == '/') {
+ ++dir;
+ --dirlen;
+ }
+
+ /* chop off leading slashes */
+ while (pathlen && pathspec[0] == '/') {
+ ++pathspec;
+ --pathlen;
+ }
+
+ if (!dirlen || !pathlen)
+ return 0;
+
+ if (pathlen <= dirlen)
+ return 0;
+
+ last_slash = strrchr(pathspec, '/');
+
+ if (!last_slash)
+ return 0;
+
+ while (*last_slash == '/' && last_slash > pathspec)
+ --last_slash;
+
+ if (last_slash == pathspec)
+ return 0;
+
+ pathlen = last_slash - pathspec + 1;
+ /*printf("real dirlen = %d real pathlen = %d\n",
+ dirlen, pathlen);*/
+ if (pathlen != dirlen)
+ return 0;
+
+ /* todo - intelligently skip multiple slashes mid-path */
+ return !strncmp(dir, pathspec, dirlen);
+}
+
+
+static int
+domainStarted(virDomainPtr mojaDomain, const char *path, int mode)
+{
+ char dom_uuid[42];
+ char *xml;
+ xmlDocPtr doc;
+ xmlNodePtr cur, devices, child, serial;
+ xmlAttrPtr attr, attr_mode, attr_path;
+
+ if (!mojaDomain)
+ return -1;
+
+ virDomainGetUUIDString(mojaDomain, dom_uuid);
+
+ xml = virDomainGetXMLDesc(mojaDomain, 0);
+ // printf("%s\n", xml);
+ // @todo: free mojaDomain
+
+ // parseXML output
+ doc = xmlParseMemory(xml, strlen(xml));
+ xmlFree(xml);
+ cur = xmlDocGetRootElement(doc);
+
+ if (cur == NULL) {
+ fprintf(stderr, "Empty doc\n");
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ if (xmlStrcmp(cur->name, (const xmlChar *) "domain")) {
+ fprintf(stderr, "no domain?\n");
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ devices = cur->xmlChildrenNode;
+ for (devices = cur->xmlChildrenNode; devices != NULL;
+ devices = devices->next) {
+ if (xmlStrcmp(devices->name, (const xmlChar *) "devices")) {
+ continue;
+ }
+
+ for (child = devices->xmlChildrenNode; child != NULL;
+ child = child->next) {
+
+ if ((!mode && xmlStrcmp(child->name, (const xmlChar *) "serial")) ||
+ (mode && xmlStrcmp(child->name, (const xmlChar *) "channel"))) {
+ continue;
+ }
+
+ attr = xmlHasProp(child, (const xmlChar *)"type");
+ if (attr == NULL)
+ continue;
+
+ if (xmlStrcmp(attr->children->content,
+ (const xmlChar *) "unix")) {
+ continue;
+ }
+
+ for (serial = child->xmlChildrenNode; serial != NULL;
+ serial = serial->next) {
+ if (xmlStrcmp(serial->name,
+ (const xmlChar *) "source")) {
+ continue;
+ }
+
+ attr_mode = xmlHasProp(serial, (const xmlChar *)"mode");
+ attr_path = xmlHasProp(serial, (const xmlChar *)"path");
+
+ if (!attr_path || !attr_mode)
+ continue;
+
+ if (xmlStrcmp(attr_mode->children->content,
+ (const xmlChar *) "bind"))
+ continue;
+
+ if (path && !is_in_directory(path, (const char *)
+ attr_path->children->content))
+ continue;
+
+ domain_sock_setup(dom_uuid, (const char *)
+ attr_path->children->content);
+ }
+ }
+ }
+
+ xmlFreeDoc(doc);
+ return 0;
+}
+
+static int
+registerExisting(virConnectPtr vp, const char *path, int mode)
+{
+ int *d_ids = NULL;
+ int d_count, x;
+ virDomainPtr dom;
+ virDomainInfo d_info;
+
+ errno = EINVAL;
+ if (!vp)
+ return -1;
+
+ d_count = virConnectNumOfDomains(vp);
+ if (d_count <= 0) {
+ if (d_count == 0) {
+ /* Successful, but no domains running */
+ errno = 0;
+ return 0;
+ }
+ goto out_fail;
+ }
+
+ d_ids = malloc(sizeof (int) * d_count);
+ if (!d_ids)
+ goto out_fail;
+
+ if (virConnectListDomains(vp, d_ids, d_count) < 0)
+ goto out_fail;
+
+ /* Ok, we have the domain IDs - let's get their names and states */
+ for (x = 0; x < d_count; x++) {
+ dom = virDomainLookupByID(vp, d_ids[x]);
+ if (!dom) {
+ /* XXX doom */
+ goto out_fail;
+ }
+
+ if (virDomainGetInfo(dom, &d_info) < 0) {
+ /* XXX no info for the domain?!! */
+ virDomainFree(dom);
+ goto out_fail;
+ }
+
+ if (d_info.state != VIR_DOMAIN_SHUTOFF &&
+ d_info.state != VIR_DOMAIN_CRASHED)
+ domainStarted(dom, path, mode);
+
+ virDomainFree(dom);
+ }
+
+ out_fail:
+ free(d_ids);
+ return 0;
+}
+
+static int
+domainStopped(virDomainPtr mojaDomain)
+{
+ char dom_uuid[42];
+
+ if (!mojaDomain)
+ return -1;
+
+ virDomainGetUUIDString(mojaDomain, dom_uuid);
+ domain_sock_close(dom_uuid);
+
+ return 0;
+}
+
+
+struct event_args {
+ char *uri;
+ char *path;
+ int mode;
+ int wake_fd;
+};
+
+static void
+connectClose(virConnectPtr conn ATTRIBUTE_UNUSED,
+ int reason,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ switch (reason) {
+ case VIR_CONNECT_CLOSE_REASON_ERROR:
+ dbg_printf(2, "Connection closed due to I/O error\n");
+ break;
+ case VIR_CONNECT_CLOSE_REASON_EOF:
+ dbg_printf(2, "Connection closed due to end of file\n");
+ break;
+ case VIR_CONNECT_CLOSE_REASON_KEEPALIVE:
+ dbg_printf(2, "Connection closed due to keepalive timeout\n");
+ break;
+ case VIR_CONNECT_CLOSE_REASON_CLIENT:
+ dbg_printf(2, "Connection closed due to client request\n");
+ break;
+ default:
+ dbg_printf(2, "Connection closed due to unknown reason\n");
+ break;
+ };
+ run = 0;
+}
+
+int
+myDomainEventCallback1(virConnectPtr conn,
+ virDomainPtr dom, int event, int detail, void *opaque)
+{
+ struct event_args *args = (struct event_args *)opaque;
+
+ if (event == VIR_DOMAIN_EVENT_STARTED ||
+ event == VIR_DOMAIN_EVENT_STOPPED) {
+ virDomainRef(dom);
+ if (event == VIR_DOMAIN_EVENT_STARTED) {
+ domainStarted(dom, args->path, args->mode);
+ virDomainFree(dom);
+ if (write(args->wake_fd, "x", 1) != 1) {
+ dbg_printf(3, "Unable to wake up thread\n");
+ }
+ } else if (event == VIR_DOMAIN_EVENT_STOPPED) {
+ domainStopped(dom);
+ virDomainFree(dom);
+ }
+ }
+
+ return 0;
+}
+
+
+static void *
+event_thread(void *arg)
+{
+ struct event_args *args = (struct event_args *)arg;
+ virConnectPtr dconn = NULL;
+ int callback1ret = -1;
+
+ dbg_printf(3, "Libvirt event listener starting\n");
+ if (args->uri)
+ dbg_printf(3," * URI: %s\n", args->uri);
+ if (args->path)
+ dbg_printf(3," * Socket path: %s\n", args->path);
+ dbg_printf(3," * Mode: %s\n", args->mode ? "VMChannel" : "Serial");
+
+ if (virEventRegisterDefaultImpl() < 0) {
+ dbg_printf(1, "Failed to register default event impl\n");
+ goto out;
+ }
+
+ dconn = virConnectOpen(args->uri);
+ if (!dconn) {
+ dbg_printf(1, "Error connecting to libvirt\n");
+ goto out;
+ }
+
+ virConnectRegisterCloseCallback(dconn, connectClose, NULL, NULL);
+
+ DEBUG0("Registering domain event cbs");
+
+ registerExisting(dconn, args->path, args->mode);
+
+ callback1ret =
+ virConnectDomainEventRegister(dconn, myDomainEventCallback1, arg, NULL);
+
+ if (callback1ret != -1) {
+ if (virConnectSetKeepAlive(dconn, 5, 5) < 0) {
+ dbg_printf(1, "Failed to start keepalive protocol\n");
+ run = 0;
+ }
+ while (run) {
+ if (virEventRunDefaultImpl() < 0) {
+ dbg_printf(1, "RunDefaultImpl Failed\n");
+ }
+ }
+
+ DEBUG0("Deregistering event handlers");
+ virConnectDomainEventDeregister(dconn, myDomainEventCallback1);
+ }
+
+ DEBUG0("Closing connection");
+ if (dconn && virConnectClose(dconn) < 0) {
+ dbg_printf(1, "error closing libvirt connection\n");
+ }
+
+out:
+ free(args->uri);
+ free(args->path);
+ free(args);
+ return NULL;
+}
+
+
+int
+start_event_listener(const char *uri, const char *path, int mode, int *wake_fd)
+{
+ struct event_args *args = NULL;
+ int wake_pipe[2];
+
+ virInitialize();
+
+ args = malloc(sizeof(*args));
+ if (!args)
+ return -1;
+ memset(args, 0, sizeof(*args));
+
+ if (pipe2(wake_pipe, O_CLOEXEC) < 0) {
+ goto out_fail;
+ }
+
+ if (uri) {
+ args->uri = strdup(uri);
+ if (args->uri == NULL)
+ goto out_fail;
+ }
+
+ if (path) {
+ args->path = strdup(path);
+ if (args->path == NULL)
+ goto out_fail;
+ }
+
+ args->mode = mode;
+ //args->p_tid = pthread_self();
+ *wake_fd = wake_pipe[0];
+ args->wake_fd = wake_pipe[1];
+
+ run = 1;
+
+ return pthread_create(&event_tid, NULL, event_thread, args);
+
+out_fail:
+ free(args->uri);
+ free(args->path);
+ free(args);
+ return -1;
+}
+
+
+int
+stop_event_listener(void)
+{
+ run = 0;
+ //pthread_cancel(event_tid);
+ pthread_join(event_tid, NULL);
+ event_tid = 0;
+
+ return 0;
+}
+
+
diff --git a/agents/virt/server/virt-sockets.c b/agents/virt/server/virt-sockets.c
new file mode 100644
index 0000000..7f36f62
--- /dev/null
+++ b/agents/virt/server/virt-sockets.c
@@ -0,0 +1,242 @@
+#include "config.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <list.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "serial.h"
+#include "debug.h"
+#include "simpleconfig.h"
+
+struct socket_list {
+ list_head();
+ char *domain_name;
+ char *socket_path;
+ int socket_fd;
+};
+
+static struct socket_list *socks = NULL;
+static pthread_mutex_t sock_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+static int
+connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout)
+{
+ int ret, flags, err;
+ unsigned l;
+ fd_set rfds, wfds;
+ struct timeval tv;
+
+ /*
+ Set up non-blocking connect
+ */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ return -1;
+ }
+
+ ret = connect(fd, dest, len);
+
+ if ((ret < 0) && (errno != EINPROGRESS))
+ return -1;
+
+ if (ret == 0)
+ goto done;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (!FD_ISSET(fd, &rfds) && !FD_ISSET(fd, &wfds)) {
+ errno = EIO;
+ return -1;
+ }
+
+ l = sizeof(err);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR,
+ (void *)&err, &l) < 0) {
+ return -1;
+ }
+
+ if (err != 0) {
+ errno = err;
+ return -1;
+ }
+
+done:
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+
+int
+domain_sock_setup(const char *domain, const char *socket_path)
+{
+ struct sockaddr_un *sun = NULL;
+ struct socket_list *node = NULL;
+ socklen_t sun_len;
+ int sock = -1;
+
+ sun_len = sizeof(*sun) + strlen(socket_path) + 1;
+ sun = malloc(sun_len);
+ if (!sun)
+ return -1;
+
+ memset((char *)sun, 0, sun_len);
+ sun->sun_family = PF_LOCAL;
+ strncpy(sun->sun_path, socket_path, sizeof(sun->sun_path) - 1);
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0)
+ goto out_fail;
+
+ if (connect_nb(sock, (struct sockaddr *)sun, SUN_LEN(sun), 3) < 0)
+ goto out_fail;
+
+ free(sun);
+ sun = NULL;
+
+ node = calloc(1, sizeof(*node));
+ if (!node)
+ goto out_fail;
+
+ node->domain_name = strdup(domain);
+ if (!node->domain_name)
+ goto out_fail;
+
+ node->socket_path = strdup(socket_path);
+ if (!node->socket_path)
+ goto out_fail;
+
+ node->socket_fd = sock;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_insert(&socks, node);
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ dbg_printf(3, "Registered %s on %d\n", domain, sock);
+ return 0;
+
+out_fail:
+ if (node) {
+ free(node->domain_name);
+ if (node->socket_path)
+ free(node->socket_path);
+ free(node);
+ }
+ free(sun);
+ if (sock >= 0)
+ close(sock);
+ return -1;
+}
+
+
+int
+domain_sock_close(const char *domain)
+{
+ struct socket_list *node = NULL;
+ struct socket_list *dead = NULL;
+ int x;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_for(&socks, node, x) {
+ if (!strcasecmp(domain, node->domain_name)) {
+ list_remove(&socks, node);
+ dead = node;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ if (dead) {
+ dbg_printf(3, "Unregistered %s, fd%d\n",
+ dead->domain_name,
+ dead->socket_fd);
+ close(dead->socket_fd);
+ free(dead->domain_name);
+ free(dead->socket_path);
+ free(dead);
+ }
+
+ return 0;
+}
+
+
+int
+domain_sock_fdset(fd_set *fds, int *max)
+{
+ struct socket_list *node = NULL;
+ int x = 0, _max = -1;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_for(&socks, node, x) {
+ FD_SET(node->socket_fd, fds);
+ if (node->socket_fd > _max)
+ _max = node->socket_fd;
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ if (max)
+ *max = _max;
+
+ return x;
+}
+
+
+int
+domain_sock_name(int fd, char *outbuf, size_t buflen)
+{
+ struct socket_list *node = NULL;
+ int ret = 1, x = 0;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_for(&socks, node, x) {
+ if (node->socket_fd == fd) {
+ snprintf(outbuf, buflen, "%s", node->domain_name);
+ ret = 0;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ return ret;
+}
+
+
+int
+domain_sock_cleanup(void)
+{
+ struct socket_list *dead= NULL;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ while(socks) {
+ dead = socks;
+ list_remove(&socks, dead);
+ close(dead->socket_fd);
+ free(dead->domain_name);
+ free(dead->socket_path);
+ free(dead);
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ return 0;
+}
+
diff --git a/agents/virt/server/virt.c b/agents/virt/server/virt.c
new file mode 100644
index 0000000..d4c94e9
--- /dev/null
+++ b/agents/virt/server/virt.c
@@ -0,0 +1,630 @@
+/*
+ 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.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <libvirt/libvirt.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "debug.h"
+#include "uuid-test.h"
+#include "virt.h"
+
+static int
+_compare_virt(const void *_left, const void *_right)
+{
+ virt_state_t *left = (virt_state_t *)_left,
+ *right = (virt_state_t *)_right;
+
+ return strcasecmp(left->v_name, right->v_name);
+}
+
+
+static void
+_free_dom_list(virDomainPtr *dom_list, int len) {
+ int x;
+
+ if (!dom_list || len <= 0)
+ return;
+ for (x = 0 ; x < len; x++)
+ virDomainFree(dom_list[x]);
+
+ free(dom_list);
+}
+
+
+virt_list_t *vl_get(virConnectPtr *vp, int vp_count, int my_id)
+{
+ virt_list_t *vl = NULL;
+ int d_count = 0;
+ int i;
+
+ errno = EINVAL;
+ if (!vp || vp_count < 1)
+ return NULL;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ int x;
+ virDomainPtr *dom_list;
+ virt_list_t *new_vl;
+
+ int ret = virConnectListAllDomains(vp[i], &dom_list, 0);
+ if (ret == 0)
+ continue;
+
+ if (ret < 0) {
+ int saved_errno = errno;
+ dbg_printf(2, "Error: virConnectListAllDomains: %d %d\n",
+ ret, saved_errno);
+ if (vl)
+ free(vl);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ d_count += ret;
+ new_vl = realloc(vl, sizeof(uint32_t) + sizeof(virt_state_t) * d_count);
+ if (!new_vl) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+ vl = new_vl;
+ vl->vm_count = d_count;
+
+ /* Ok, we have the domain IDs - let's get their names and states */
+ for (x = 0; x < ret; x++) {
+ char *d_name;
+ virDomainInfo d_info;
+ char d_uuid[MAX_DOMAINNAME_LENGTH];
+ virDomainPtr dom = dom_list[x];
+
+ if (!(d_name = (char *)virDomainGetName(dom))) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+
+ if (virDomainGetUUIDString(dom, d_uuid) != 0) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+
+ if (virDomainGetInfo(dom, &d_info) < 0) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+
+ /* Store the name & state */
+ strncpy(vl->vm_states[x].v_name, d_name, MAX_DOMAINNAME_LENGTH);
+ strncpy(vl->vm_states[x].v_uuid, d_uuid, MAX_DOMAINNAME_LENGTH);
+ vl->vm_states[x].v_state.s_state = d_info.state;
+ vl->vm_states[x].v_state.s_owner = my_id;
+ }
+
+ _free_dom_list(dom_list, ret);
+ }
+ /* No domains found */
+ if (!vl)
+ return NULL;
+
+ /* We have all the locally running domains & states now */
+ /* Sort */
+ qsort(&vl->vm_states[0], vl->vm_count, sizeof(vl->vm_states[0]),
+ _compare_virt);
+ return vl;
+}
+
+int
+vl_add(virt_list_t **vl, virt_state_t *vm) {
+ virt_list_t *new_vl;
+ size_t oldlen;
+ size_t newlen;
+
+ if (!vl)
+ return -1;
+
+ if (!*vl) {
+ *vl = malloc(sizeof(uint32_t) + sizeof(virt_state_t));
+ if (!*vl)
+ return -1;
+ (*vl)->vm_count = 1;
+ memcpy(&(*vl)->vm_states[0], vm, sizeof(virt_state_t));
+ return 0;
+ }
+
+ oldlen = sizeof(uint32_t) + sizeof(virt_state_t) * (*vl)->vm_count;
+ newlen = oldlen + sizeof(virt_state_t);
+
+ new_vl = malloc(newlen);
+ if (!new_vl)
+ return -1;
+
+ memcpy(new_vl, *vl, oldlen);
+ memcpy(&new_vl->vm_states[(*vl)->vm_count], vm, sizeof(virt_state_t));
+ new_vl->vm_count++;
+
+ free(*vl);
+ *vl = new_vl;
+ return 0;
+}
+
+int vl_remove_by_owner(virt_list_t **vl, uint32_t owner) {
+ int i;
+ int removed = 0;
+ virt_list_t *new_vl;
+
+ if (!vl || !*vl)
+ return 0;
+
+ for (i = 0 ; i < (*vl)->vm_count ; i++) {
+ if ((*vl)->vm_states[i].v_state.s_owner == owner) {
+ dbg_printf(2, "Removing %s\n", (*vl)->vm_states[i].v_name);
+ memset(&(*vl)->vm_states[i].v_state, 0,
+ sizeof((*vl)->vm_states[i].v_state));
+ (*vl)->vm_states[i].v_name[0] = 0xff;
+ (*vl)->vm_states[i].v_uuid[0] = 0xff;
+ removed++;
+ }
+ }
+
+ if (!removed)
+ return 0;
+
+ qsort(&(*vl)->vm_states[0], (*vl)->vm_count, sizeof((*vl)->vm_states[0]),
+ _compare_virt);
+ (*vl)->vm_count -= removed;
+
+ new_vl = realloc(*vl, sizeof(uint32_t) + (sizeof(virt_state_t) * ((*vl)->vm_count)));
+ if (new_vl)
+ *vl = new_vl;
+ return removed;
+}
+
+
+int
+vl_update(virt_list_t **vl, virt_state_t *vm) {
+ virt_state_t *v = NULL;
+
+ if (!vl)
+ return -1;
+
+ if (!*vl)
+ return vl_add(vl, vm);
+
+ if (strlen(vm->v_uuid) > 0)
+ v = vl_find_uuid(*vl, vm->v_uuid);
+
+ if (v == NULL && strlen(vm->v_name) > 0)
+ v = vl_find_name(*vl, vm->v_name);
+
+ if (v == NULL) {
+ dbg_printf(2, "Adding new entry for VM %s\n", vm->v_name);
+ vl_add(vl, vm);
+ } else {
+ dbg_printf(2, "Updating entry for VM %s\n", vm->v_name);
+ memcpy(&v->v_state, &vm->v_state, sizeof(v->v_state));
+ }
+
+ return 0;
+}
+
+
+void
+vl_print(virt_list_t *vl)
+{
+ int x;
+
+ printf("%-24.24s %-36.36s %-5.5s %-5.5s\n", "Domain", "UUID",
+ "Owner", "State");
+ printf("%-24.24s %-36.36s %-5.5s %-5.5s\n", "------", "----",
+ "-----", "-----");
+
+ if (!vl || !vl->vm_count)
+ return;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ printf("%-24.24s %-36.36s %-5.5d %-5.5d\n",
+ vl->vm_states[x].v_name,
+ vl->vm_states[x].v_uuid,
+ vl->vm_states[x].v_state.s_owner,
+ vl->vm_states[x].v_state.s_state);
+ }
+}
+
+
+virt_state_t *
+vl_find_name(virt_list_t *vl, const char *name)
+{
+ int x;
+
+ if (!vl || !name || !vl->vm_count)
+ return NULL;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ if (!strcasecmp(vl->vm_states[x].v_name, name))
+ return &vl->vm_states[x];
+ }
+
+ return NULL;
+}
+
+
+virt_state_t *
+vl_find_uuid(virt_list_t *vl, const char *uuid)
+{
+ int x;
+
+ if (!vl || !uuid || !vl->vm_count)
+ return NULL;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ if (!strcasecmp(vl->vm_states[x].v_uuid, uuid))
+ return &vl->vm_states[x];
+ }
+
+ return NULL;
+}
+
+
+void
+vl_free(virt_list_t *old)
+{
+ free(old);
+}
+
+
+static inline int
+wait_domain(const char *vm_name, virConnectPtr vp, int timeout)
+{
+ int tries = 0;
+ int response = 1;
+ int ret;
+ virDomainPtr vdp;
+ virDomainInfo vdi;
+ int uuid_check;
+
+ uuid_check = is_uuid(vm_name);
+
+ if (uuid_check) {
+ vdp = virDomainLookupByUUIDString(vp, (const char *)vm_name);
+ } else {
+ vdp = virDomainLookupByName(vp, vm_name);
+ }
+ if (!vdp)
+ return 0;
+
+ /* Check domain liveliness. If the domain is still here,
+ we return failure, and the client must then retry */
+ /* XXX On the xen 3.0.4 API, we will be able to guarantee
+ synchronous virDomainDestroy, so this check will not
+ be necessary */
+ do {
+ if (++tries > timeout)
+ break;
+
+ sleep(1);
+ if (uuid_check) {
+ vdp = virDomainLookupByUUIDString(vp, (const char *)vm_name);
+ } else {
+ vdp = virDomainLookupByName(vp, vm_name);
+ }
+ if (!vdp) {
+ dbg_printf(2, "Domain no longer exists\n");
+ response = 0;
+ break;
+ }
+
+ memset(&vdi, 0, sizeof(vdi));
+ ret = virDomainGetInfo(vdp, &vdi);
+ virDomainFree(vdp);
+ if (ret < 0)
+ continue;
+
+ if (vdi.state == VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "Domain has been shut off\n");
+ response = 0;
+ break;
+ }
+
+ dbg_printf(4, "Domain still exists (state %d) after %d seconds\n",
+ vdi.state, tries);
+ } while (1);
+
+ return response;
+}
+
+
+int
+vm_off(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL;
+ virDomainInfo vdi;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+ int ret = -1;
+ int i;
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp)
+ break;
+ }
+
+ if (!vdp) {
+ dbg_printf(2, "[virt:OFF] Domain %s does not exist\n", vm_name);
+ return 1;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF)
+ {
+ dbg_printf(2, "[virt:OFF] Nothing to do - "
+ "domain %s is already off\n",
+ vm_name);
+ virDomainFree(vdp);
+ return 0;
+ }
+
+ syslog(LOG_NOTICE, "Destroying domain %s\n", vm_name);
+ dbg_printf(2, "[virt:OFF] Calling virDomainDestroy for %s\n", vm_name);
+
+ ret = virDomainDestroy(vdp);
+ virDomainFree(vdp);
+
+ if (ret < 0) {
+ syslog(LOG_NOTICE,
+ "Failed to destroy domain %s: %d\n", vm_name, ret);
+ dbg_printf(2, "[virt:OFF] Failed to destroy domain: %s %d\n",
+ vm_name, ret);
+ return 1;
+ }
+
+ if (ret) {
+ syslog(LOG_NOTICE, "Domain %s still exists; fencing failed\n",
+ vm_name);
+ dbg_printf(2,
+ "[virt:OFF] Domain %s still exists; fencing failed\n",
+ vm_name);
+ return 1;
+ }
+
+ dbg_printf(2, "[virt:OFF] Success for %s\n", vm_name);
+ return 0;
+}
+
+
+int
+vm_on(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL;
+ virDomainInfo vdi;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+ int ret = -1;
+ int i;
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp)
+ break;
+ }
+
+ if (!vdp) {
+ dbg_printf(2, "[virt:ON] Domain %s does not exist\n", vm_name);
+ return 1;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state != VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "Nothing to do - domain %s is already running\n",
+ vm_name);
+ virDomainFree(vdp);
+ return 0;
+ }
+
+ syslog(LOG_NOTICE, "Starting domain %s\n", vm_name);
+ dbg_printf(2, "[virt:ON] Calling virDomainCreate for %s\n", vm_name);
+
+ ret = virDomainCreate(vdp);
+ virDomainFree(vdp);
+
+ if (ret < 0) {
+ syslog(LOG_NOTICE, "Failed to start domain %s: %d\n", vm_name, ret);
+ dbg_printf(2, "[virt:ON] virDomainCreate() failed for %s: %d\n",
+ vm_name, ret);
+ return 1;
+ }
+
+ if (ret) {
+ syslog(LOG_NOTICE, "Domain %s did not start\n", vm_name);
+ dbg_printf(2, "[virt:ON] Domain %s did not start\n", vm_name);
+ return 1;
+ }
+
+ syslog(LOG_NOTICE, "Domain %s started\n", vm_name);
+ dbg_printf(2, "[virt:ON] Success for %s\n", vm_name);
+ return 0;
+}
+
+
+int
+vm_status(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL;
+ virDomainInfo vdi;
+ int ret = 0;
+ int i;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp)
+ break;
+ }
+
+ if (!vdp) {
+ dbg_printf(2, "[virt:STATUS] Unknown VM %s - return OFF\n", vm_name);
+ return RESP_OFF;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "[virt:STATUS] VM %s is OFF\n", vm_name);
+ ret = RESP_OFF;
+ }
+
+ if (vdp)
+ virDomainFree(vdp);
+ return ret;
+}
+
+
+int
+vm_reboot(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL, nvdp;
+ virDomainInfo vdi;
+ char *domain_desc;
+ virConnectPtr vcp = NULL;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+ int ret;
+ int i;
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp) {
+ vcp = vp[i];
+ break;
+ }
+ }
+
+ if (!vdp || !vcp) {
+ dbg_printf(2,
+ "[virt:REBOOT] Nothing to do - domain %s does not exist\n",
+ vm_name);
+ return 1;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "[virt:REBOOT] Nothing to do - domain %s is off\n",
+ vm_name);
+ virDomainFree(vdp);
+ return 0;
+ }
+
+ syslog(LOG_NOTICE, "Rebooting domain %s\n", vm_name);
+ dbg_printf(5, "[virt:REBOOT] Rebooting domain %s...\n", vm_name);
+
+ domain_desc = virDomainGetXMLDesc(vdp, 0);
+
+ if (!domain_desc) {
+ dbg_printf(5, "[virt:REBOOT] Failed getting domain description "
+ "from libvirt for %s...\n", vm_name);
+ }
+
+ dbg_printf(2, "[virt:REBOOT] Calling virDomainDestroy(%p) for %s\n",
+ vdp, vm_name);
+
+ ret = virDomainDestroy(vdp);
+ if (ret < 0) {
+ dbg_printf(2,
+ "[virt:REBOOT] virDomainDestroy() failed for %s: %d/%d\n",
+ vm_name, ret, errno);
+
+ if (domain_desc)
+ free(domain_desc);
+ virDomainFree(vdp);
+ return 1;
+ }
+
+ ret = wait_domain(vm_name, vcp, 15);
+
+ if (ret) {
+ syslog(LOG_NOTICE, "Domain %s still exists; fencing failed\n", vm_name);
+ dbg_printf(2,
+ "[virt:REBOOT] Domain %s still exists; fencing failed\n",
+ vm_name);
+
+ if (domain_desc)
+ free(domain_desc);
+ virDomainFree(vdp);
+ return 1;
+ }
+
+ if (!domain_desc)
+ return 0;
+
+ /* 'on' is not a failure */
+ ret = 0;
+
+ dbg_printf(3, "[[ XML Domain Info ]]\n");
+ dbg_printf(3, "%s\n[[ XML END ]]\n", domain_desc);
+
+ dbg_printf(2, "[virt:REBOOT] Calling virDomainCreateLinux() for %s\n",
+ vm_name);
+
+ nvdp = virDomainCreateLinux(vcp, domain_desc, 0);
+ if (nvdp == NULL) {
+ /* More recent versions of libvirt or perhaps the
+ * KVM back-end do not let you create a domain from
+ * XML if there is already a defined domain description
+ * with the same name that it knows about. You must
+ * then call virDomainCreate() */
+ dbg_printf(2,
+ "[virt:REBOOT] virDomainCreateLinux() failed for %s; "
+ "Trying virDomainCreate()\n",
+ vm_name);
+
+ if (virDomainCreate(vdp) < 0) {
+ syslog(LOG_NOTICE, "Could not restart %s\n", vm_name);
+ dbg_printf(1, "[virt:REBOOT] Failed to recreate guest %s!\n",
+ vm_name);
+ }
+ }
+
+ free(domain_desc);
+ virDomainFree(vdp);
+ return ret;
+}
diff --git a/agents/virt/server/virt.h b/agents/virt/server/virt.h
new file mode 100644
index 0000000..1d9140c
--- /dev/null
+++ b/agents/virt/server/virt.h
@@ -0,0 +1,62 @@
+/*
+ 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.
+*/
+
+#ifndef _VIRT_H
+#define _VIRT_H
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <libvirt/libvirt.h>
+
+#include "xvm.h"
+
+typedef struct {
+ uint32_t s_owner;
+ int32_t s_state;
+} vm_state_t;
+
+typedef struct {
+ char v_name[MAX_DOMAINNAME_LENGTH + 1];
+ char v_uuid[MAX_DOMAINNAME_LENGTH + 1];
+ vm_state_t v_state;
+} virt_state_t;
+
+/**
+ This is stored in our private checkpoint section.
+ */
+typedef struct _virt_list {
+ uint32_t vm_count;
+ virt_state_t vm_states[0];
+} virt_list_t;
+
+virt_list_t *vl_get(virConnectPtr *vp, int vp_count, int my_id);
+void vl_print(virt_list_t *vl);
+void vl_free(virt_list_t *old);
+virt_state_t *vl_find_uuid(virt_list_t *vl, const char *name);
+virt_state_t *vl_find_name(virt_list_t *vl, const char *name);
+int vl_add(virt_list_t **vl, virt_state_t *vm);
+int vl_update(virt_list_t **vl, virt_state_t *vm);
+int vl_remove_by_owner(virt_list_t **vl, uint32_t owner);
+
+int vm_off(virConnectPtr *vp, int vp_count, const char *vm_name);
+int vm_on(virConnectPtr *vp, int vp_count, const char *vm_name);
+int vm_status(virConnectPtr *vp, int vp_count, const char *vm_name);
+int vm_reboot(virConnectPtr *vp, int vp_count, const char *vm_name);
+
+#endif
diff --git a/agents/virt/server/vsock.c b/agents/virt/server/vsock.c
new file mode 100644
index 0000000..1b88fa5
--- /dev/null
+++ b/agents/virt/server/vsock.c
@@ -0,0 +1,565 @@
+/*
+ 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 <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <nss.h>
+#include <sys/socket.h>
+#include <linux/vm_sockets.h>
+
+/* Local includes */
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "tcp_listener.h"
+#include "debug.h"
+#include "fdops.h"
+
+#define NAME "vsock"
+#define VSOCK_VERSION "0.2"
+
+#define VSOCK_MAGIC 0xa32d27c1e
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != VSOCK_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+typedef struct _vsock_options {
+ char *key_file;
+ int cid;
+ unsigned int port;
+ unsigned int hash;
+ unsigned int auth;
+ unsigned int flags;
+} vsock_options;
+
+
+typedef struct _vsock_info {
+ uint64_t magic;
+ void *priv;
+ map_object_t *map;
+ history_info_t *history;
+ char key[MAX_KEY_LEN];
+ vsock_options args;
+ const fence_callbacks_t *cb;
+ ssize_t key_len;
+ int listen_sock;
+} vsock_info;
+
+
+struct vsock_hostlist_arg {
+ map_object_t *map;
+ int cid;
+ int fd;
+};
+
+
+static int get_peer_cid(int fd, uint32_t *peer_cid) {
+ struct sockaddr_vm svm;
+ socklen_t len;
+ int ret;
+
+ if (!peer_cid)
+ return -1;
+
+ len = sizeof(svm);
+ ret = getpeername(fd, (struct sockaddr *) &svm, &len);
+ if (ret < 0) {
+ printf("Error getting peer CID: %s\n", strerror(errno));
+ return -1;
+ }
+
+ *peer_cid = svm.svm_cid;
+ return 0;
+}
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ fence_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+vsock_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct vsock_hostlist_arg *arg = (struct vsock_hostlist_arg *) priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+ uint32_t peer_cid = 0;
+ char peer_cid_str[24];
+
+ ret = get_peer_cid(arg->fd, &peer_cid);
+ if (ret < 0) {
+ printf("Unable to get peer CID: %s\n", strerror(errno));
+ peer_cid_str[0] = '\0';
+ } else
+ snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid);
+
+ /* Noops if auth == AUTH_NONE */
+
+ if (map_check2(arg->map, peer_cid_str, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+vsock_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ char val = (char) RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &val, 1, &tv);
+}
+
+
+static int
+vsock_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+do_fence_request_vsock(int fd, fence_req_t *req, vsock_info *info)
+{
+ char response = 1;
+ struct vsock_hostlist_arg arg;
+ uint32_t peer_cid = 0;
+ char peer_cid_str[24];
+ int ret;
+
+ ret = get_peer_cid(fd, &peer_cid);
+ if (ret < 0) {
+ printf("Unable to get peer CID: %s\n", strerror(errno));
+ return -1;
+ }
+
+ snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid);
+
+ /* Noops if auth == AUTH_NONE */
+ if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) {
+ printf("CID %u Failed to respond to challenge\n", peer_cid);
+ close(fd);
+ return -1;
+ }
+
+ ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10);
+ if (ret <= 0) {
+ printf("Remote CID %u failed challenge\n", peer_cid);
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(2, "Request %d seqno %d target %s from CID %u\n",
+ req->request, req->seqno, req->domain, peer_cid);
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, peer_cid_str,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, peer_cid_str,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, peer_cid_str,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->map;
+ arg.fd = fd;
+
+ vsock_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(vsock_hostlist, &arg, info->priv);
+ vsock_hostlist_end(arg.fd);
+ break;
+ }
+
+ dbg_printf(3, "Sending response to caller CID %u...\n", peer_cid);
+ if (_write_retry(fd, &response, 1, NULL) < 0)
+ perror("write");
+
+ history_record(info->history, req);
+
+ if (fd != -1)
+ close(fd);
+
+ return 1;
+}
+
+
+static int
+vsock_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ vsock_info *info;
+ fence_req_t data;
+ fd_set rfds;
+ int n;
+ int client_fd;
+ int ret;
+ struct timeval tv;
+
+ if (timeout != NULL)
+ memcpy(&tv, timeout, sizeof(tv));
+ else {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ }
+
+ info = (vsock_info *) c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ FD_SET(info->listen_sock, &rfds);
+
+ n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout);
+ if (n <= 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+
+ client_fd = accept(info->listen_sock, NULL, NULL);
+ if (client_fd < 0) {
+ perror("accept");
+ return -1;
+ }
+
+ dbg_printf(3, "Accepted vsock client...\n");
+
+ ret = _read_retry(client_fd, &data, sizeof(data), &tv);
+ if (ret != sizeof(data)) {
+ dbg_printf(3, "Invalid request (read %d bytes)\n", ret);
+ close(client_fd);
+ return 0;
+ }
+
+ swab_fence_req_t(&data);
+
+ if (!verify_request(&data, info->args.hash, info->key, info->key_len)) {
+ printf("Key mismatch; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ dbg_printf(3, "Request %d seqno %d domain %s\n",
+ data.request, data.seqno, data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ printf("We just did this request; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ switch(info->args.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ printf("VSOCK request\n");
+ do_fence_request_vsock(client_fd, &data, info);
+ break;
+ default:
+ printf("XXX Unhandled authentication\n");
+ }
+
+ return 0;
+}
+
+
+static int
+vsock_config(config_object_t *config, vsock_options *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/vsock/@key_file",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", value);
+ args->key_file = strdup(value);
+ } else {
+ args->key_file = strdup(DEFAULT_KEY_FILE);
+ if (!args->key_file) {
+ dbg_printf(1, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ args->hash = DEFAULT_HASH;
+ if (sc_get(config, "listeners/vsock/@hash",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for hash\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = HASH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = HASH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = HASH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = HASH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported hash: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->auth = DEFAULT_AUTH;
+ if (sc_get(config, "listeners/vsock/@auth",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for auth\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = AUTH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = AUTH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = AUTH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = AUTH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported auth: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->port = DEFAULT_MCAST_PORT;
+ if (sc_get(config, "listeners/vsock/@port",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for port\n", value);
+ args->port = atoi(value);
+ if (args->port <= 0) {
+ dbg_printf(1, "Invalid port: %s\n", value);
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+
+static int
+vsock_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ vsock_info *info;
+ int listen_sock, ret;
+ struct sockaddr_vm svm;
+
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ printf("Could not initialize NSS\n");
+ return 1;
+ }
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+
+ info->priv = priv;
+ info->cb = cb;
+ info->map = map;
+
+ ret = vsock_config(config, &info->args);
+ if (ret < 0)
+ perror("vsock_config");
+ else if (ret > 0)
+ printf("%d errors found during vsock listener configuration\n", ret);
+
+ if (ret != 0) {
+ if (info->args.key_file)
+ free(info->args.key_file);
+ free(info);
+ return -1;
+ }
+
+ if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
+ info->key_len = read_key_file(info->args.key_file,
+ info->key, sizeof(info->key));
+ if (info->key_len < 0) {
+ printf("Could not read %s; operating without "
+ "authentication\n", info->args.key_file);
+ info->args.auth = AUTH_NONE;
+ info->args.hash = HASH_NONE;
+ info->key_len = 0;
+ }
+ }
+
+ listen_sock = socket(PF_VSOCK, SOCK_STREAM, 0);
+ if (listen_sock < 0)
+ goto out_fail;
+
+ memset(&svm, 0, sizeof(svm));
+ svm.svm_family = AF_VSOCK;
+ svm.svm_cid = VMADDR_CID_ANY;
+ svm.svm_port = info->args.port;
+
+ if (bind(listen_sock, (struct sockaddr *) &svm, sizeof(svm)) < 0)
+ goto out_fail;
+
+ if (listen(listen_sock, 1) < 0)
+ goto out_fail;
+
+ info->magic = VSOCK_MAGIC;
+ info->listen_sock = listen_sock;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ return 0;
+
+out_fail:
+ printf("Could not set up listen socket: %s\n", strerror(errno));
+ if (listen_sock >= 0)
+ close(listen_sock);
+ if (info->args.key_file)
+ free(info->args.key_file);
+ free(info);
+ return -1;
+}
+
+
+static int
+vsock_shutdown(listener_context_t c)
+{
+ vsock_info *info = (vsock_info *)c;
+
+ VALIDATE(info);
+ info->magic = 0;
+ history_wipe(info->history);
+ free(info->history);
+ free(info->args.key_file);
+ close(info->listen_sock);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t vsock_plugin = {
+ .name = NAME,
+ .version = VSOCK_VERSION,
+ .init = vsock_init,
+ .dispatch = vsock_dispatch,
+ .cleanup = vsock_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &vsock_plugin;
+}