[Cluster-devel] cluster/fence agents/Makefile man/Makefile age ...

lhh at sourceware.org lhh at sourceware.org
Thu Oct 5 16:11:41 UTC 2006


CVSROOT:	/cvs/cluster
Module name:	cluster
Changes by:	lhh at sourceware.org	2006-10-05 16:11:36

Modified files:
	fence/agents   : Makefile 
	fence/man      : Makefile 
Added files:
	fence/agents/xvm: Makefile Makefile.tmp README TODO fence_xvm.c 
	                  fence_xvmd.c ip_lookup.c ip_lookup.h mcast.c 
	                  mcast.h options.c options.h simple_auth.c 
	                  simple_auth.h tcp.c tcp.h virt.c virt.h 
	                  vm_states.c xvm.h 
	fence/man      : fence_xvm.8 fence_xvmd.8 

Log message:
	Implementation of client/server based Xen Virtual Machine (xvm) fencing.
	This allows fencing of a virtual machine from any other virtual machine in
	the cluster (regardless of the physical host) which shares the same
	private key, either based on UUID or Xen domain name.  Please see README
	and TODO before posting feature requests.

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/Makefile.diff?cvsroot=cluster&r1=1.13&r2=1.14
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/Makefile.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/Makefile.tmp.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/README.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/TODO.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/fence_xvm.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/fence_xvmd.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/ip_lookup.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/ip_lookup.h.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/mcast.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/mcast.h.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/options.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/options.h.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/simple_auth.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/simple_auth.h.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/tcp.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/tcp.h.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/virt.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/virt.h.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/vm_states.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/xvm.h.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/man/fence_xvm.8.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/man/fence_xvmd.8.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/man/Makefile.diff?cvsroot=cluster&r1=1.9&r2=1.10

--- cluster/fence/agents/Makefile	2006/08/11 15:18:07	1.13
+++ cluster/fence/agents/Makefile	2006/10/05 16:11:36	1.14
@@ -34,6 +34,7 @@
 	${MAKE} -C vixel all
 	${MAKE} -C wti all
 	# ${MAKE} -C xcat all
+	${MAKE} -C xvm all
 	# ${MAKE} -C zvm all
 
 install: all
@@ -58,6 +59,7 @@
 	${MAKE} -C vixel install
 	${MAKE} -C wti install
 	# ${MAKE} -C xcat install
+	${MAKE} -C xvm install
 	# ${MAKE} -C zvm install
 
 clean:
@@ -82,4 +84,5 @@
 	${MAKE} -C vixel clean
 	${MAKE} -C wti clean
 	# ${MAKE} -C xcat clean
+	${MAKE} -C xvm clean
 	# ${MAKE} -C zvm clean
/cvs/cluster/cluster/fence/agents/xvm/Makefile,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/Makefile
+++ -	2006-10-05 16:11:37.892567000 +0000
@@ -0,0 +1,54 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) 2006 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.
+##
+###############################################################################
+###############################################################################
+
+top_srcdir=../..
+include ${top_srcdir}/make/defines.mk
+
+TARGETS=fence_xvm fence_xvmd
+
+fence_xvm_SOURCE = fence_xvm.c mcast.c ip_lookup.c simple_auth.c tcp.c \
+		   options.c
+fence_xvmd_SOURCE= fence_xvmd.c mcast.c simple_auth.c tcp.c virt.c \
+		   options.c vm_states.c
+
+
+INCLUDE=-I${top_srcdir}/include -I${top_srcdir}/config \
+	-I/usr/include/openais -I/usr/include/libvirt \
+	-I/usr/include/nss3 -I/usr/include/nspr4
+
+CFLAGS+=-DFENCE_RELEASE_NAME=\"${RELEASE}\" \
+	-Wall -Werror -Wstrict-prototypes -Wshadow -ggdb
+
+LIBS+=-L../../../cman/lib -L../../../ccs/lib -L/usr/lib/openais \
+      -lccs -lcman -ldlm -lnss3 -lvirt -lSaCkpt
+
+all: ${TARGETS}
+
+fence_xvm: ${fence_xvm_SOURCE:.c=.o}
+	gcc -o $@ $^ $(LIBS)
+
+fence_xvmd: ${fence_xvmd_SOURCE:.c=.o}
+	gcc -o $@ $^ $(LIBS)
+
+%.o: %.c
+	gcc $(CFLAGS) -c -o $@ $^ $(INCLUDES)
+
+clean:
+	rm -f $(TARGETS) *~ *.o
+
+install: all
+	if [ ! -d ${sbindir} ]; then \
+		install -d ${sbindir}; \
+	fi
+	install -m755 ${TARGETS} ${sbindir}
+
+
/cvs/cluster/cluster/fence/agents/xvm/Makefile.tmp,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/Makefile.tmp
+++ -	2006-10-05 16:11:38.014035000 +0000
@@ -0,0 +1,40 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##  
+##  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.
+##
+###############################################################################
+###############################################################################
+
+TARGET= fence_ipmilan
+
+fence_ipmilan_SOURCE= expect.c ipmilan.c
+
+top_srcdir=../..
+
+
+INCLUDE= -I${top_srcdir}/include -I${top_srcdir}/config
+
+
+include ${top_srcdir}/make/defines.mk
+
+CFLAGS+= -DFENCE_RELEASE_NAME=\"${RELEASE}\" -DFENCE
+
+all: ${TARGET}
+
+fence_ipmilan: ${fence_ipmilan_SOURCE:.c=.o}
+	${CC} ${CFLAGS} ${LDFLAGS} ${fence_ipmilan_SOURCE:.c=.o} -o $@
+
+install: all
+	if [ ! -d ${sbindir} ]; then \
+		install -d ${sbindir}; \
+	fi
+	install -m755 ${TARGET} ${sbindir}
+
+clean:
+	rm -f *.o ${TARGET}
/cvs/cluster/cluster/fence/agents/xvm/README,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/README
+++ -	2006-10-05 16:11:38.126821000 +0000
@@ -0,0 +1,124 @@
+I. Fence_xvm - the Xen 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_xvmd - The Xen virtual machine fencing host
+
+Fence_xvmd is a daemon which runs on physical hosts (e.g. in domain0)
+of the cluster hosting the Xen virtual cluster.  It listens on a port
+for multicast traffic from Xen 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_xvmd.
+
+NOTE: fence_xvmd *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 AIS Checkpointing API, which is provided by OpenAIS.  Every
+few seconds, fence_xvmd queries the Xen Hypervisor via libvirt and
+stores any local VM states in a checkpoint.  In the event of a
+physical node failure (which consequently causes the failure of one
+or more Xen guests), we can then read the checkpoint section
+corresponding to the guest we need to fence to find out the previous
+owner.  With that information, we can then check with CMAN to see if
+the last-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_xvmd 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 checkpoint
+  (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 checkpoint 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_xvmd 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_xvmd 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_xvmd on all dom0s
+
+Configure fence_xvm on the domU cluster...
+
+rest...tbd
+
/cvs/cluster/cluster/fence/agents/xvm/TODO,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/TODO
+++ -	2006-10-05 16:11:38.232522000 +0000
@@ -0,0 +1,42 @@
+High Priority / Blockers:
+
+* Nothing at this time.
+
+Medium Priority:
+
+* Enable retrieval of options from ccs rather than only on the
+command line for fence_xvmd.
+
+* Need to add ability for fence_xvmd to forcefully fence the host
+dom0 if it's not responding.  Medium because it should not be the
+default behavior since fencing a host can affect multiple domains
+across potentially multiple domU clusters.  This will be a server-
+side configuration option; domUs will not be able to override it.
+
+* Support multiple authentication keys in fence_xvmd simultaneously
+so that we can fence multiple clusters with only one instance of
+fence_xvmd running on a given dom0.
+
+Low Priority:
+
+* Turn README in to man pages.
+
+* Make sure CMAN is running and/or restart/reconnect if CMAN goes
+away and comes back.  (If CMAN dies, we have big problems anyway)
+
+* Add SSL connection support.  (Challenge/response on a trusted
+network should be okay.)
+
+* Destroy/Create doesn't work very well with pygrub-loaded or
+xenguest-install images. (Maybe just wait for solid virDomainReboot
+operation based on Xen 3.0.4 API).  For now, rely on external 
+management to restart the domain (a person, VM manager, or failover
+manager like rgmanager)
+
+* Make sure addresses contained in the multicast packet are always
+in network-byte order.  Low because it will be unlikely that the
+host-byte ordering of a domU and its dom0 will be different.
+
+* Make sure node IDs and VM states stored in openais checkpoints
+are in network-byte order and swap back/forth if not.
+
/cvs/cluster/cluster/fence/agents/xvm/fence_xvm.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/fence_xvm.c
+++ -	2006-10-05 16:11:38.642695000 +0000
@@ -0,0 +1,358 @@
+/*
+  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.
+*/
+/*
+ * @file fence_xvmd.c: Implementation of server daemon for Xen virtual
+ * machine fencing.  This uses SA AIS CKPT b.1.0 checkpointing API to 
+ * store virtual machine states.
+ *
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+#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 "virt.h"
+#include "ip_lookup.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "tcp.h"
+#include "mcast.h"
+
+
+int
+tcp_wait_connect(int lfd, int retry_tenths)
+{
+	int fd;
+	fd_set rfds;
+	int n;
+	struct timeval tv;
+
+	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;
+}
+
+
+int
+tcp_exchange(int fd, fence_auth_type_t auth, void *key,
+	      size_t key_len, int timeout)
+{
+	char ret;
+	fd_set rfds;
+	struct timeval tv;
+
+	/* Ok, we're connected */
+	if (tcp_challenge(fd, auth, key, key_len, timeout) <= 0) {
+		/* Challenge failed */
+		printf("Invalid response to challenge\n");
+		return 0;
+	}
+
+	/* Now they'll send us one, so we need to respond here */
+	if (tcp_response(fd, auth, key, key_len, timeout) <= 0) {
+		printf("Invalid response to challenge\n");
+		return 0;
+	}
+
+	printf("TCP Exchange + Authentication done... \n");
+
+	FD_ZERO(&rfds);
+	FD_SET(fd, &rfds);
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+
+	ret = 1;
+	if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0)
+		return -1;
+
+	/* Read return code */
+	read(fd, &ret, 1);
+	close(fd);
+	if (ret == 0)
+		printf("Remote: Operation was successful\n");
+	else
+		printf("Remote: Operation failed\n");
+	return ret;
+}
+
+
+int
+send_multicast_packets(ip_list_t *ipl, fence_xvm_args_t *args, 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->family) {
+			printf("Ignoring %s: wrong family\n", ipa->ipa_address);
+			continue;
+		}
+
+		if (args->family == PF_INET) {
+			mc_sock = ipv4_send_sk(ipa->ipa_address, args->addr,
+					       args->port,
+					       (struct sockaddr *)&tgt4,
+					       sizeof(struct sockaddr_in));
+			tgt = (struct sockaddr *)&tgt4;
+			tgt_len = sizeof(tgt4);
+			
+		} else if (args->family == PF_INET6) {
+			mc_sock = ipv6_send_sk(ipa->ipa_address, args->addr,
+					       args->port,
+					       (struct sockaddr *)&tgt6,
+					       sizeof(struct sockaddr_in6));
+			tgt = (struct sockaddr *)&tgt6;
+			tgt_len = sizeof(tgt6);
+		} else {
+			printf("Unsupported family %d\n", args->family);
+			return -1;
+		}
+
+		if (mc_sock < 0)
+			continue;
+
+		/* Build our packet */
+		memset(&freq, 0, sizeof(freq));
+		strncpy((char *)freq.domain, args->domain,
+			sizeof(freq.domain));
+		freq.request = args->op;
+		freq.hashtype = args->hash;
+
+		/* Store source address */
+		if (ipa->ipa_family == PF_INET) {
+			freq.addrlen = sizeof(struct in_addr);
+			/* XXX Swap order for in_addr ? XXX */
+			inet_pton(PF_INET, ipa->ipa_address, freq.address);
+		} else if (ipa->ipa_family == PF_INET6) {
+			freq.addrlen = sizeof(struct in6_addr);
+			inet_pton(PF_INET6, ipa->ipa_address, freq.address);
+		}
+
+		freq.flags = 0;
+		if (args->flags & F_USE_UUID)
+			freq.flags |= RF_UUID;
+		freq.family = ipa->ipa_family;
+		freq.port = args->port;
+
+		sign_request(&freq, key, key_len);
+
+		printf("Sending to %s via %s\n", args->addr,
+		       ipa->ipa_address);
+
+		sendto(mc_sock, &freq, sizeof(freq), 0,
+		       (struct sockaddr *)tgt, tgt_len);
+
+		close(mc_sock);
+	}
+
+	return 0;
+}
+
+
+/* TODO: Clean this up!!! */
+int
+fence_xen_domain(fence_xvm_args_t *args)
+{
+	ip_list_t ipl;
+	char key[4096];
+	int lfd, key_len, fd;
+	int attempts = 0;
+
+	key_len = read_key_file(args->key_file, key, sizeof(key));
+	if (key_len < 0) {
+		printf("Key file unreadable!\n");
+		return 1;
+	}
+
+	/* Do the real work */
+	if (ip_build_list(&ipl) < 0) {
+		printf("Error building IP address list\n");
+		return 1;
+	}
+
+	switch (args->auth) {
+		case AUTH_NONE:
+		case AUTH_SHA1:
+		case AUTH_SHA256:
+		case AUTH_SHA512:
+			if (args->family == PF_INET) {
+				lfd = ipv4_listen(args->port, 10);
+			} else {
+				lfd = ipv6_listen(args->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));
+		return 1;
+	}
+
+	attempts = args->timeout * 10 / args->retr_time;
+
+	do {
+		if (send_multicast_packets(&ipl, args, key, key_len)) {
+			return -1;
+		}
+
+		switch (args->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:
+			return 1;
+		}
+
+		break;
+	} while (--attempts);
+
+	if (fd < 0) {
+		if (attempts <= 0) {
+			printf("Timed out waiting for response\n");
+			return 1;
+		}
+		printf("Fencing failed: %s\n", strerror(errno));
+		return -1;
+	}
+
+	switch (args->auth) {
+		case AUTH_NONE:
+		case AUTH_SHA1:
+		case AUTH_SHA256:
+		case AUTH_SHA512:
+			return tcp_exchange(fd, args->auth, key, key_len,
+					    args->timeout);
+			break;
+		/* case AUTH_X509: 
+			return ssl_exchange(...); */
+		default:
+			return 1;
+	}
+
+	return 1;
+}
+
+
+int
+main(int argc, char **argv)
+{
+	fence_xvm_args_t args;
+	char *my_options = "di:a:p:r:C:c:k:H:uo:t:?h";
+
+	args_init(&args);
+	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);
+		args_usage(argv[0], my_options, 1);
+		exit(0);
+	}
+
+	if (args.flags & F_VERSION) {
+		printf("%s %s\n", basename(argv[0]), XVM_VERSION);
+#ifdef FENCE_RELEASE_NAME
+		printf("fence release %s\n", FENCE_RELEASE_NAME);
+#endif
+		exit(0);
+	}
+
+	args_finalize(&args);
+
+	if (args.flags & F_DEBUG)
+		args_print(&args);
+
+	/* Additional validation here */
+	if (!args.domain) {
+		printf("No domain specified!\n");
+		args.flags |= F_ERR;
+	}
+
+	if (args.flags & F_ERR) {
+		args_usage(argv[0], my_options, (argc == 1));
+		exit(1);
+	}
+
+	/* 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;
+	}
+
+	return fence_xen_domain(&args);
+}
/cvs/cluster/cluster/fence/agents/xvm/fence_xvmd.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/fence_xvmd.c
+++ -	2006-10-05 16:11:38.800051000 +0000
@@ -0,0 +1,652 @@
+/*
+  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 <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 <virterror.h>
+#include <libcman.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 "virt.h"
+
+static int running = 1;
+
+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(sin));
+		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 (tcp_response(fd, auth, key, key_len, 10) <= 0) {
+		printf("Failed to respond to challenge\n");
+		close(fd);
+		return -1;
+	}
+
+	if (tcp_challenge(fd, auth, key, key_len, 10) <= 0) {
+		printf("Remote failed challenge\n");
+		close(fd);
+		return -1;
+	}
+	return fd;
+}
+
+
+int
+do_notify_caller_tcp(fence_req_t *req, fence_auth_type_t auth,
+		     void *key, size_t key_len, char response)
+{
+	int fd;
+
+	fd = connect_tcp(req, auth, key, key_len);
+	if (fd < 0)
+		goto out;
+
+	if (write(fd, &response, 1) < 0) {
+		perror("write");
+	}
+out:
+	if (fd != -1) {
+		close(fd);
+		return 0;
+	}
+
+	return -1;
+}
+
+
+void
+raise_error(virConnectPtr vp)
+{
+	virErrorPtr vep;
+
+	vep = virConnGetLastError(vp);
+	if (!vep) {
+		printf("Error: Unable to retrieve error from connection!\n");
+		return;
+	}
+
+	printf("Error: libvirt #%d domain %d: %s\n", vep->code,
+	       vep->domain, vep->message);
+}
+
+
+static inline virDomainPtr
+get_domain(fence_req_t *req, virConnectPtr vp)
+{
+	if (req->flags & RF_UUID) {
+		return virDomainLookupByUUIDString(vp,
+					(const char *)req->domain);
+	}
+
+	return virDomainLookupByName(vp, (const char *)req->domain);
+}
+
+
+int
+do_fence_request_tcp(fence_req_t *req, fence_auth_type_t auth,
+		     void *key, size_t key_len, virConnectPtr vp)
+{
+	int fd, ret = -1;
+	virDomainPtr vdp;
+	char response = 1;
+	char *domain_desc;
+
+	if (!(vdp = get_domain(req, vp)))
+		goto out;
+
+	fd = connect_tcp(req, auth, key, key_len);
+	if (fd < 0)
+		goto out;
+
+	switch(req->request) {
+	case FENCE_NULL:
+		printf("NULL operation: returning failure\n");
+		response = 1;
+		break;
+	case FENCE_OFF:
+		printf("Destroying domain %s...\n", (char *)req->domain);
+		ret = virDomainDestroy(vdp);
+		if (ret < 0) {
+			/* raise_error(vp); */
+			break;
+		} else { 
+			sleep(1);
+		}
+
+		/* 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 */
+		vdp = get_domain(req, vp);
+		if (!vdp) {
+			response = 0;	/* Success! */
+		} else {
+			virDomainFree(vdp);
+		}
+		break;
+	case FENCE_REBOOT:
+		printf("Rebooting domain %s...\n",
+		       (char *)req->domain);
+		domain_desc = virDomainGetXMLDesc(vdp, 0);
+		ret = virDomainDestroy(vdp);
+		if (ret < 0) {
+			if (domain_desc)
+				free(domain_desc);
+			break;
+		} else {
+			/* Give it time for the operation to complete */
+			sleep(1);
+		}
+
+		/* 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 */
+		vdp = get_domain(req, vp);
+		if (!vdp) {
+			response = 0;	/* Success! */
+		} else {
+			virDomainFree(vdp);
+			ret = 1;	/* Failed to kill it */
+		}
+
+		/* Recreate the domain if possible */
+		if (ret == 0 && domain_desc) {
+			/* Success */
+			virDomainCreateLinux(vp, domain_desc, 0);
+		}
+
+		if (domain_desc)
+			free(domain_desc);
+		break;
+	}
+	
+	if (write(fd, &response, 1) < 0) {
+		perror("write");
+	}
+out:
+	if (fd != -1)
+		close(fd);
+
+	return 1;
+}
+
+
+int
+virt_list_update(virConnectPtr vp, virt_list_t **vl, int my_id)
+{
+	virt_list_t *list = NULL;
+
+	list = vl_get(vp, my_id);
+	if (!list)
+		return -1;
+
+	if (*vl)
+		vl_free(*vl);
+	*vl = list;
+
+	return 0;
+}
+
+
+int
+get_cman_ids(cman_handle_t ch, int *my_id, int *high_id)
+{
+	int max_nodes;
+	int actual;
+	cman_node_t *nodes = NULL;
+	cman_node_t me;
+	int high = 0, ret = -1, x, _local = 0;
+
+	if (!my_id && !high_id)
+		return 0;
+
+	if (!ch) {
+		_local = 1;
+		ch = cman_init(NULL);
+	}
+	if (!ch)
+		return -1;
+
+	max_nodes = cman_get_node_count(ch);
+	if (max_nodes <= 0)
+		goto out;
+
+	if (my_id) {
+		memset(&me, 0, sizeof(me));
+		if (cman_get_node(ch, CMAN_NODEID_US, &me) < 0)
+			goto out;
+		*my_id = me.cn_nodeid;
+	}
+
+	if (!high_id) {
+		ret = 0;
+		goto out;
+	}
+
+	nodes = malloc(sizeof(cman_node_t) * max_nodes);
+	if (!nodes)
+		goto out;
+	memset(nodes, 0, sizeof(cman_node_t) * max_nodes);
+
+	if (cman_get_nodes(ch, max_nodes, &actual, nodes) < 0)
+		goto out;
+
+	for (x = 0; x < actual; x++)
+		if (nodes[x].cn_nodeid > high)
+			high = nodes[x].cn_nodeid;
+
+	*high_id = high;
+
+	ret = 0;
+out:
+	if (nodes)
+		free(nodes);
+	if (ch && _local)
+		cman_finish(ch);
+	return ret;
+}
+
+
+int
+get_domain_state_ckpt(void *hp, unsigned char *domain, vm_state_t *state)
+{
+	errno = EINVAL;
+
+	if (!hp || !domain || !state || !strlen((char *)domain))
+		return -1;
+	if (!strcmp("Domain-0", (char *)domain))
+		return -1;
+
+	return ckpt_read(hp, (char *)domain, state, sizeof(*state));
+}
+
+
+void
+store_domains_by_name(void *hp, virt_list_t *vl)
+{
+	int x;
+
+	if (!vl)
+		return;
+
+	for (x = 0; x < vl->vm_count; x++) {
+		if (!strcmp(DOMAIN0NAME, vl->vm_states[x].v_name))
+			continue;
+		printf("Storing %s\n", vl->vm_states[x].v_name);
+		ckpt_write(hp, vl->vm_states[x].v_name, 
+			   &vl->vm_states[x].v_state,
+			   sizeof(vm_state_t));
+	}
+}
+
+
+void
+store_domains_by_uuid(void *hp, virt_list_t *vl)
+{
+	int x;
+
+	if (!vl)
+		return;
+
+	for (x = 0; x < vl->vm_count; x++) {
+		if (!strcmp(DOMAIN0UUID, vl->vm_states[x].v_uuid))
+			continue;
+		printf("Storing %s\n", vl->vm_states[x].v_uuid);
+		ckpt_write(hp, vl->vm_states[x].v_uuid, 
+			   &vl->vm_states[x].v_state,
+			   sizeof(vm_state_t));
+	}
+}
+
+
+static void
+handle_remote_domain(cman_handle_t ch, void *h, fence_req_t *data,
+		     fence_auth_type_t auth, void *key, size_t key_len,
+		     int my_id)
+{
+	vm_state_t vst;
+	int high_id;
+	int fenced;
+	uint64_t fence_time;
+	char ret = 1;
+	cman_node_t node;
+
+	if (get_domain_state_ckpt(h, data->domain, &vst) < 0) {
+		printf("XXX I have no record of that domain\n");
+		return;
+	}
+
+	printf("Evaluating Domain: %s   Last Owner: %d   State %d\n",
+	       data->domain, vst.s_owner, vst.s_state);
+			
+	if (get_cman_ids(ch, NULL, &high_id) < 0)
+		return;
+
+	if (my_id == high_id && vst.s_owner != my_id) {
+
+		memset(&node, 0, sizeof(node));
+		cman_get_node(ch, vst.s_owner, &node);
+		if (node.cn_nodeid == 0) {
+			printf("Node %d does not exist\n", vst.s_owner);
+			return;
+		}
+
+		if (node.cn_member) {
+			printf("Node %d is online - not taking action\n",
+			       vst.s_owner);
+			return;
+		}
+
+		fenced = 0;
+		cman_get_fenceinfo(ch, vst.s_owner, &fence_time, &fenced, NULL);
+		if (fenced == 0) {
+			printf("Node %d is dead but not fenced - not "
+			       "taking action\n", vst.s_owner);
+			return;
+		}
+
+		printf("Node %d is dead & fenced\n", vst.s_owner);
+		ret = 0;
+					
+	} else if (vst.s_owner == my_id) {
+		printf("I am the last owner of the domain\n");
+		ret = 0;
+	}
+
+	if (!ret) {
+		switch(auth) {
+		case AUTH_NONE:
+		case AUTH_SHA1:
+		case AUTH_SHA256:
+		case AUTH_SHA512:
+			printf("Plain TCP request\n");
+			do_notify_caller_tcp(data, auth, key, key_len, ret);
+			break;
+		default:
+			printf("XXX Unhandled authentication\n");
+		}
+	}
+}
+
+
+int
+xvmd_loop(void *h, int fd, fence_xvm_args_t *args,
+	  void *key, size_t key_len)
+{
+	fd_set rfds;
+	struct timeval tv;
+	struct sockaddr_in sin;
+	int len;
+	int n;
+	int my_id;
+	socklen_t slen;
+	fence_req_t data;
+	virConnectPtr vp;
+	virt_list_t *vl;
+	virt_state_t *dom;
+	cman_handle_t ch;
+
+	vp = virConnectOpen(NULL);
+	if (!vp) {
+		perror("virConnectOpen");
+		return -1;
+	}
+
+	ch = cman_init(NULL);
+
+	get_cman_ids(ch, &my_id, NULL);
+	printf("My Node ID = %d\n", my_id);
+
+	vl = vl_get(vp, my_id);
+	vl_print(vl);
+
+	while (running) {
+		FD_ZERO(&rfds);
+		FD_SET(fd, &rfds);
+		tv.tv_sec = 10;
+		tv.tv_usec = 0;
+
+		n = select(fd+1, &rfds, NULL, NULL, &tv);
+		if (n < 0)
+			continue;
+		if (n == 0) {
+			virt_list_update(vp, &vl, my_id);
+			vl_print(vl);
+			/* Store information here */
+			if (args->flags & F_USE_UUID) 
+				store_domains_by_uuid(h, vl);
+			else
+				store_domains_by_name(h, vl);
+			continue;
+		}
+
+		slen = sizeof(sin);
+		len = recvfrom(fd, &data, sizeof(data), 0,
+			       (struct sockaddr *)&sin, &slen);
+		
+		if (len <= 0) {
+			perror("recvfrom");
+			continue;
+		}
+
+		if (!verify_request(&data, args->hash, key, key_len)) {
+			printf("Key mismatch; dropping packet\n");
+			continue;
+		}
+
+		if ((args->flags & F_USE_UUID) &&
+		    !(data.flags & RF_UUID)) {
+			printf("Dropping packet: Request to fence by "
+			       "name while using UUIDs\n");
+			continue;
+		}
+
+		if (!(args->flags & F_USE_UUID) &&
+		    (data.flags & RF_UUID)) {
+			printf("Dropping packet: Request to fence by "
+			       "UUID while using names\n");
+			continue;
+		}
+
+		printf("Request to fence: %s\n", data.domain);
+
+		/* Do a quick update to make sure we're current */
+		virt_list_update(vp, &vl, my_id);
+		vl_print(vl);
+
+		if (args->flags & F_USE_UUID)
+			dom = vl_find_uuid(vl, (char *)data.domain);
+		else
+			dom = vl_find_name(vl, (char *)data.domain);
+		if (!dom) {
+			handle_remote_domain(ch, h, &data, args->auth,
+					     key, key_len, my_id);
+			continue;
+		}
+
+		printf("%s is running locally\n",
+		       (char *)data.domain);
+
+		switch(args->auth) {
+		case AUTH_NONE:
+		case AUTH_SHA1:
+		case AUTH_SHA256:
+		case AUTH_SHA512:
+			printf("Plain TCP request\n");
+			do_fence_request_tcp(&data, args->auth, key,
+					     key_len, vp);
+			break;
+		default:
+			printf("XXX Unhandled authentication\n");
+		}
+	}
+
+	cman_finish(ch);
+	virConnectClose(vp);
+
+	return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+	fence_xvm_args_t args;
+	int mc_sock;
+	char key[4096];
+	int key_len;
+	char *my_options = "dfi:a:p:C:c:k:u?h";
+	void *h;
+
+	args_init(&args);
+	args_get_getopt(argc, argv, my_options, &args);
+	args_finalize(&args);
+	if (args.flags & F_DEBUG)
+		args_print(&args);
+
+	if (args.flags & F_ERR) {
+		args_usage(argv[0], my_options, 0);
+		return 1;
+	}
+
+	if (args.flags & F_HELP) {
+		args_usage(argv[0], my_options, 0);
+		return 0;
+	}
+
+	if (args.flags & F_VERSION) {
+		printf("%s %s\n", basename(argv[0]), XVM_VERSION);
+#ifdef FENCE_RELEASE_NAME
+		printf("fence release %s\n", FENCE_RELEASE_NAME);
+#endif
+		exit(0);
+	}
+
+	key_len = read_key_file(args.key_file, key, sizeof(key));
+	if (key_len < 0) {
+		printf("Could not read key file\n");
+		return 1;
+	}
+
+	/* Fork in to background */
+	/* XXX need to wait for child to successfully start before
+	   exiting... */
+	if (!(args.flags & F_FOREGROUND))
+		daemon(0,0);
+
+	if (virInitialize() != 0) {
+		printf("Could not initialize libvirt\n");
+		return 1;
+	}
+
+	/* 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;
+	}
+
+	h = ckpt_init("vm_states", 262144, 4096, 64, 10);
+	if (!h) {
+		printf("Could not initialize Checkpoint\n");
+		return 1;
+	}
+
+	if (args.family == PF_INET)
+		mc_sock = ipv4_recv_sk(args.addr, args.port);
+	else
+		mc_sock = ipv6_recv_sk(args.addr, args.port);
+	if (mc_sock < 0) {
+		printf("Could not set up multicast listen socket\n");
+		return 1;
+	}
+
+	xvmd_loop(h, mc_sock, &args, key, key_len);
+
+	return 0;
+}
/cvs/cluster/cluster/fence/agents/xvm/ip_lookup.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/ip_lookup.c
+++ -	2006-10-05 16:11:38.978191000 +0000
@@ -0,0 +1,298 @@
+/*
+  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 <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"
+
+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 = malloc(sizeof(*ipa));
+	memset(ipa, 0, sizeof(*ipa));
+	ipa->ipa_family = family;
+	ipa->ipa_address = strdup(ipaddr);
+
+	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];
+	int x, fd, len;
+
+	fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+	if (fd < 0) {
+		perror("socket");
+		exit(1);
+	}
+
+	send_addr_dump(fd, family);
+	memset(buf, 0, sizeof(buf));
+	x = recvfrom(fd, buf, sizeof(buf), 0, NULL, 0);
+	if (x < 0) {
+		perror("recvfrom");
+		return -1;
+	}
+
+	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 = (struct rtattr *)((void *)ifa + sizeof(*ifa));
+		len -= sizeof(*ifa);
+		do {
+			/* Make sure we've got a valid rtaddr field */
+			if (!RTA_OK(rta, len))
+				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) {
+				printf("label: %s\n", (char *)RTA_DATA(rta));
+			}
+
+			nrta = RTA_NEXT(rta, len);
+			if (!nrta)
+				break;
+
+			len -= ((void *)nrta - (void *)rta);
+			rta = nrta;
+		} while (RTA_OK(rta, len));
+
+		nh = NLMSG_NEXT(nh, x);
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+int
+ip_search(ip_list_t *ipl, char *ip_name)
+{
+	ip_addr_t *ipa;
+
+	ipa = ipl->tqh_first;
+	for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) {
+		if (!strcmp(ip_name, ipa->ipa_address)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+
+int
+ip_free_list(ip_list_t *ipl)
+{
+	ip_addr_t *ipa;
+
+	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)
+{
+	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;
+
+	/* 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;
+}
+
/cvs/cluster/cluster/fence/agents/xvm/ip_lookup.h,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/ip_lookup.h
+++ -	2006-10-05 16:11:39.178965000 +0000
@@ -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
/cvs/cluster/cluster/fence/agents/xvm/mcast.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/mcast.c
+++ -	2006-10-05 16:11:39.374973000 +0000
@@ -0,0 +1,356 @@
+/*
+  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 <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"
+
+/** 
+  Sets up a multicast receive socket
+ */
+int
+ipv4_recv_sk(char *addr, int port)
+{
+	int sock;
+	struct ip_mreq mreq;
+	struct sockaddr_in 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 *
+	 ********************************/
+	sock = socket(PF_INET, SOCK_DGRAM, 0);
+	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.
+	 */
+	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 */
+	mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+	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;
+	}
+
+	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;
+	}
+
+	/* 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 *
+	 *************************/
+	sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	/*
+	 * Join Multicast group.
+	 */
+	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.
+	 */
+	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.
+	 */
+	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));
+
+	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 *
+	 ********************************/
+	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;
+	}
+
+	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
+	 */
+	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;
+	}
+
+	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(&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 *
+	 *************************/
+	sock = socket(PF_INET6, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	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.
+	 */
+	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));
+
+	return sock;
+}
/cvs/cluster/cluster/fence/agents/xvm/mcast.h,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/mcast.h
+++ -	2006-10-05 16:11:39.619967000 +0000
@@ -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);
+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);
+int ipv6_send_sk(char *src_addr, char *addr, int port,
+		 struct sockaddr *src, socklen_t slen);
+
+#endif
/cvs/cluster/cluster/fence/agents/xvm/options.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/options.c
+++ -	2006-10-05 16:11:39.735763000 +0000
@@ -0,0 +1,631 @@
+/*
+  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 <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>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "mcast.h"
+#include "options.h"
+
+
+/* Private structure for commandline / stdin fencing args */
+struct arg_info {
+	char opt;
+	char *opt_desc;
+	char *stdin_opt;
+	char *desc;
+	void (*assign)(fence_xvm_args_t *, struct arg_info *, char *);
+};
+
+
+/* Assignment functions */
+
+static inline void
+assign_debug(fence_xvm_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_DEBUG;
+		return;
+	}
+
+	args->flags |= ( !!atoi(value) ? F_DEBUG : 0);
+}
+
+
+static inline void
+assign_foreground(fence_xvm_args_t *args, struct arg_info *arg,
+		  char *value)
+{
+	args->flags |= F_FOREGROUND;
+}
+
+
+static inline void
+assign_family(fence_xvm_args_t *args, struct arg_info *arg,
+	      char *value)
+{
+	if (!strcasecmp(value, "ipv4")) {
+		args->family = PF_INET;
+	} else if (!strcasecmp(value, "ipv6")) {
+		args->family = PF_INET6;
+	} else if (!strcasecmp(value, "auto")) {
+		args->family = 0;
+	} else {
+		printf("Unsupported family: '%s'\n", value);
+		args->flags |= F_ERR;
+	}
+}
+
+
+static inline void
+assign_address(fence_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	args->addr = strdup(value);
+}
+
+
+static inline void
+assign_port(fence_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	args->port = atoi(value);
+	if (args->port <= 0 || args->port >= 65500) {
+		printf("Invalid port: '%s'\n", value);
+		args->flags |= F_ERR;
+	}
+}
+
+
+static inline void
+assign_retrans(fence_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	args->retr_time = atoi(value);
+	if (args->retr_time <= 0) {
+		printf("Invalid retransmit time: '%s'\n", value);
+		args->flags |= F_ERR;
+	}
+}
+
+static inline void
+assign_hash(fence_xvm_args_t *args, struct arg_info *arg, char *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 {
+		printf("Unsupported hash: %s\n", value);
+		args->flags |= F_ERR;
+	}
+}
+
+
+static inline void
+assign_auth(fence_xvm_args_t *args, struct arg_info *arg, char *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 {
+		printf("Unsupported auth type: %s\n", value);
+		args->flags |= F_ERR;
+	}
+}
+
+static inline void
+assign_key(fence_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	struct stat st;
+
+	args->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_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	if (!strcasecmp(value, "null")) {
+		args->op = FENCE_NULL;
+	} else if (!strcasecmp(value, "off")) {
+		args->op = FENCE_OFF;
+	} else if (!strcasecmp(value, "reboot")) {
+		args->op = FENCE_REBOOT;
+	} else {
+		printf("Unsupported operation: %s\n", value);
+		args->flags |= F_ERR;
+	}
+}
+
+
+static inline void
+assign_domain(fence_xvm_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;
+	}
+
+	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_xvm_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_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	args->timeout = atoi(value);
+	if (args->timeout <= 0) {
+		printf("Invalid timeout: '%s'\n", value);
+		args->flags |= F_ERR;
+	}
+}
+
+
+static inline void
+assign_help(fence_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	args->flags |= F_HELP;
+}
+
+
+static inline void
+assign_version(fence_xvm_args_t *args, struct arg_info *arg, char *value)
+{
+	args->flags |= F_VERSION;
+}
+
+
+/** ALL valid command line and stdin arguments for this fencing agent */
+static struct arg_info _arg_info[] = {
+	{ '\xff', NULL, "agent",
+	  "Not user serviceable",
+	  NULL },
+
+	{ '\xff', NULL, "self",
+	  "Not user serviceable", 
+	  NULL },
+
+	{ 'd', "-d", "debug",
+	  "Enable debugging mode",
+	  assign_debug },
+
+	{ 'f', "-f", NULL,
+	  "Foreground mode (do not fork)",
+	  assign_foreground },
+
+	{ 'i', "-i <family>", "ip_family",
+	  "IP Family ([auto], ipv4, ipv6)",
+	  assign_family },
+
+	{ 'a', "-a <address>", "multicast_address",
+	  "Multicast address (default=225.0.0.12 / ff02::3:1)",
+	  assign_address },
+
+	{ 'p', "-p <port>", "port",
+	  "IP port (default=1229)",
+	  assign_port },
+
+	{ 'r', "-r <retrans>", "retrans", 
+	  "Multicast retransmit time (in 1/10sec; default=20)",
+	  assign_retrans },
+
+	{ 'c', "-c <hash>", "hash",
+	  "Packet hash strength (none, sha1, [sha256], sha512)",
+	  assign_hash },
+
+	{ 'C', "-C <auth>", "auth",
+	  "Authentication (none, sha1, [sha256], sha512)",
+	  assign_auth },
+
+	{ 'k', "-k <file>", "key_file",
+	  "Shared key file (default=/etc/cluster/fence_xvm.key)",
+	  assign_key },
+
+	{ 'o', "-o <operation>", "option",
+	  "Fencing operation (null, [off], reboot)",
+	  assign_op },
+
+	{ 'H', "-H <domain>", "domain",
+	  "Xen host (domain name) to fence",
+	  assign_domain },
+
+	{ 'u', "-u", "use_uuid",
+	  "Treat <domain> as UUID instead of domain name",
+	  assign_uuid_lookup },
+
+	{ 't', "-t <timeout>", "timeout",
+	  "Fencing timeout (in seconds; default=30)",
+	  assign_timeout },
+
+	{ 'h', "-h", NULL,
+ 	  "Help",
+	  assign_help },
+
+	{ '?', "-?", NULL,
+ 	  "Help (alternate)", 
+	  assign_help },
+
+	{ 'V', "-V", NULL,
+ 	  "Display version and exit", 
+	  assign_version },
+
+
+	/* Terminator */
+	{ 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_xvm_args_t *args)
+{
+	args->addr = NULL;
+	args->domain = NULL;
+	args->key_file = DEFAULT_KEY_FILE;
+	args->op = FENCE_OFF;
+	args->hash = DEFAULT_HASH;
+	args->auth = DEFAULT_AUTH;
+	args->port = 1229;
+	args->family = PF_INET;
+	args->timeout = 30;
+	args->retr_time = 20;
+	args->flags = 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_xvm_args_t *args)
+{
+	printf("-- args @ %p --\n", args);
+	_pr_str(args->addr);
+	_pr_str(args->domain);
+	_pr_str(args->key_file);
+	_pr_int(args->op);
+	_pr_int(args->hash);
+	_pr_int(args->auth);
+	_pr_int(args->port);
+	_pr_int(args->family);
+	_pr_int(args->timeout);
+	_pr_int(args->retr_time);
+	_pr_int(args->flags);
+	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
+ */
+void
+args_usage(char *progname, char *optstr, int print_stdin)
+{
+	int x;
+	struct arg_info *arg;
+
+	if (print_stdin) {
+		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");
+	} else {
+		if (progname) {
+			printf("usage: %s [args]\n", progname);
+		} else {
+			printf("usage: fence_xvm [args]\n");
+		}
+	}
+
+	for (x = 0; x < strlen(optstr); x++) {
+		arg = find_arg_by_char(optstr[x]);
+		if (!arg)
+			continue;
+
+		if (print_stdin) {
+			if (arg && arg->stdin_opt)
+				printf("  %-20.20s %-55.55s\n",
+				       arg->stdin_opt, arg->desc);
+		} else {
+			printf("  %-20.20s %-55.55s\n", arg->opt_desc,
+			       arg->desc);
+		}
+	}
+
+	printf("\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
+ */
+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(char *optstr, fence_xvm_args_t *args)
+{
+	char in[256];
+	int line = 0;
+	char *name, *val;
+	struct arg_info *arg;
+
+	while (fgets(in, sizeof(in), stdin)) {
+		++line;
+
+		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' && 
+			     !strchr(optstr, arg->opt))) {
+			fprintf(stderr,
+				"parse warning: "
+				"illegal variable '%s' on line %d\n", name,
+				line);
+			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, char *optstr, fence_xvm_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_xvm_args_t *args)
+{
+	char *addr = NULL;
+
+	if (!args->addr) {
+		switch(args->family) {
+		case 0:
+		case PF_INET:
+			addr = IPV4_MCAST_DEFAULT;
+			break;
+		case PF_INET6:
+			addr = IPV6_MCAST_DEFAULT;
+			break;
+		default:
+			args->flags |= F_ERR;
+		break;
+		}
+	}
+
+	if (!args->addr)
+		args->addr = addr;
+
+	if (!args->addr) {
+		printf("No multicast address available\n");
+		args->flags |= F_ERR;
+	}
+
+	if (!args->addr)
+		return;
+	if (args->family)
+		return;
+
+	/* Set family */
+	if (strchr(args->addr, ':'))
+		args->family = PF_INET6;
+	if (strchr(args->addr, '.'))
+		args->family = PF_INET;
+	if (!args->family) {
+		printf("Could not determine address family\n");
+		args->flags |= F_ERR;
+	}
+}
/cvs/cluster/cluster/fence/agents/xvm/options.h,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/options.h
+++ -	2006-10-05 16:11:39.870296000 +0000
@@ -0,0 +1,53 @@
+/*
+  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 struct {
+	char *addr;
+	char *domain;
+	char *key_file;
+	fence_cmd_t op;
+	fence_hash_t hash;
+	fence_auth_type_t auth;
+	int port;
+	int family;
+	int timeout;
+	int retr_time;
+#define F_FOREGROUND	0x1
+#define F_DEBUG		0x2
+#define F_ERR		0x4
+#define F_HELP		0x8
+#define F_USE_UUID	0x10
+#define F_VERSION	0x20
+	int flags;
+} fence_xvm_args_t;
+
+
+/* Get options */
+void args_init(fence_xvm_args_t *args);
+void args_finalize(fence_xvm_args_t *args);
+
+void args_get_getopt(int argc, char **argv, char *optstr,
+		     fence_xvm_args_t *args);
+void args_get_stdin(char *optstr, fence_xvm_args_t *args);
+void args_usage(char *progname, char *optstr, int print_stdin);
+void args_print(fence_xvm_args_t *args);
+
+#endif
/cvs/cluster/cluster/fence/agents/xvm/simple_auth.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/simple_auth.c
+++ -	2006-10-05 16:11:40.053913000 +0000
@@ -0,0 +1,396 @@
+/*
+  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 <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sechash.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+
+
+void
+print_hash(unsigned char *hash, size_t hashlen)
+{
+	int x; 
+
+	for (x = 0; x < hashlen; x++)
+		printf("%02x", (hash[x]&0xff));
+}
+
+
+static void
+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;
+
+	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:
+			return;
+	}
+
+	devrand = open("/dev/urandom", O_RDONLY);
+	if (devrand >= 0) {
+		if (read(devrand, req->random, sizeof(req->random)) < 0) {
+			perror("read /dev/urandom");
+		}
+		close(devrand);
+	}
+
+	memset(hash, 0, sizeof(hash));
+	h = HASH_Create(ht);
+	if (!h)
+		return;
+
+	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));
+}
+
+
+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:
+			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:
+		return 0;
+	case HASH_SHA1:
+	case HASH_SHA256:
+	case HASH_SHA512:
+		sha_sign(req, key, key_len);
+		return 0;
+	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;
+}
+
+
+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 (read(devrand, challenge, sizeof(challenge)) < 0) {
+		perror("read /dev/urandom");
+		return 0;
+	}
+	close(devrand);
+
+	if (write(fd, challenge, sizeof(challenge)) < 0) {
+		perror("write");
+		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(fd + 1, &rfds, NULL, NULL, &tv) <= 0) {
+		perror("select");
+		return 0;
+	}
+
+	if (read(fd, response, sizeof(response)) < sizeof(response)) {
+		perror("read");
+		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;
+}
+
+
+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;
+
+	FD_ZERO(&rfds);
+	FD_SET(fd, &rfds);
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+	if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0) {
+		perror("select");
+		return 0;
+	}
+
+	if (read(fd, challenge, sizeof(challenge)) < 0) {
+		perror("read");
+		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:
+			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);
+
+	if (write(fd, hash, sizeof(hash)) < sizeof(hash)) {
+		perror("read");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+int
+tcp_challenge(int fd, fence_auth_type_t auth, void *key, size_t key_len,
+	      int timeout)
+{
+	switch(auth) {
+	case AUTH_NONE:
+		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
+tcp_response(int fd, fence_auth_type_t auth, void *key, size_t key_len,
+	     int timeout)
+{
+	switch(auth) {
+	case AUTH_NONE:
+		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;
+
+	fd = open(file, O_RDONLY);
+	if (fd < 0) {
+		return -1;
+	}
+
+	memset(key, 0, max_len);
+	p = key;
+	remain = max_len;
+
+	while (remain) {
+		nread = read(fd, p, remain);
+		if (nread < 0) {
+			close(fd);
+			return -1;
+		}
+
+		if (nread == 0)
+			break;
+		
+		p += nread;
+		remain -= nread;
+	}
+
+	close(fd);	
+	
+	return 0;
+}
/cvs/cluster/cluster/fence/agents/xvm/simple_auth.h,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/simple_auth.h
+++ -	2006-10-05 16:11:40.195474000 +0000
@@ -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 tcp_challenge(int, fence_auth_type_t, void *, size_t, int);
+int tcp_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
/cvs/cluster/cluster/fence/agents/xvm/tcp.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/tcp.c
+++ -	2006-10-05 16:11:40.296820000 +0000
@@ -0,0 +1,290 @@
+/*
+  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 <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>
+
+static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout);
+
+/**
+  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 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(uint16_t port, int backlog)
+{
+	struct sockaddr_in6 _sin6;
+	int fd, ret;
+
+	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;
+	_sin6.sin6_addr = in6addr_any;
+
+	ret = 1;
+	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ret, sizeof (ret));
+
+	ret = set_cloexec(fd);
+	if (ret < 0) {
+		close(fd);
+		return -1;
+	}
+
+	ret = bind(fd, (struct sockaddr *)&_sin6, sizeof(_sin6));
+	if (ret < 0) {
+		close(fd);
+		return -1;
+	}
+
+	if (listen(fd, backlog) < 0){
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+
+/**
+  Bind to a port on the local IPv4 stack
+
+  @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(uint16_t port, int backlog)
+{
+	struct sockaddr_in _sin;
+	int fd, ret;
+
+	fd = socket(PF_INET, SOCK_STREAM, 0);
+	if (fd < 0)
+		return -1;
+
+	_sin.sin_family = PF_INET;
+	_sin.sin_port = htons(port);
+	_sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+	ret = 1;
+	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ret, sizeof (ret));
+	
+	ret = set_cloexec(fd);
+	if (ret < 0) {
+		close(fd);
+		return -1;
+	}
+
+	ret = bind(fd, (struct sockaddr *)&_sin, sizeof(_sin));
+	if (ret < 0) {
+		close(fd);
+		return -1;
+	}
+
+	if (listen(fd, backlog) < 0){
+		close(fd);
+		return -1;
+	}
+
+	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;
+
+	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;
+	}
+	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;
+
+	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;
+	}
+
+	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);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+	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;
+			}
+
+			fcntl(fd, F_SETFL, flags);
+			return 0;
+		}
+	}
+
+	errno = EIO;
+	return -1;
+}
/cvs/cluster/cluster/fence/agents/xvm/tcp.h,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/tcp.h
+++ -	2006-10-05 16:11:40.566464000 +0000
@@ -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(uint16_t port, int backlog);
+int ipv6_listen(uint16_t port, int backlog);
+
+#endif
/cvs/cluster/cluster/fence/agents/xvm/virt.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/virt.c
+++ -	2006-10-05 16:11:40.657465000 +0000
@@ -0,0 +1,224 @@
+/*
+  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 <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <libvirt.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <errno.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);
+}
+
+
+virt_list_t *vl_get(virConnectPtr vp, int my_id)
+{
+	virt_list_t *vl = NULL;
+	int *d_ids = NULL;
+	int d_count, x;
+	char *d_name;
+	char d_uuid[MAX_DOMAINNAME_LENGTH];
+	virDomainPtr dom;
+	virDomainInfo d_info;
+
+	errno = EINVAL;
+	if (!vp)
+		return NULL;
+
+	d_count = virConnectNumOfDomains(vp);
+	if (d_count <= 0) {
+		if (d_count == 0) {
+			/* Successful, but no domains running */
+			errno = 0;
+			return NULL;
+		}
+		goto out_fail;
+	}
+
+	vl = malloc(sizeof(uint32_t) + sizeof(virt_state_t) * d_count );
+	if (!vl)
+		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;
+
+	vl->vm_count = d_count;
+
+	/* 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 (!(d_name = (char *)virDomainGetName(dom))) {
+			/* XXX no name for the domain?!! */
+			virDomainFree(dom);
+			goto out_fail;
+		}
+
+		if (virDomainGetUUIDString(dom, d_uuid) != 0) {
+			virDomainFree(dom);
+			goto out_fail;
+		}
+
+		if (virDomainGetInfo(dom, &d_info) < 0) {
+			/* XXX no info for the domain?!! */
+			virDomainFree(dom);
+			goto out_fail;
+		}
+
+		/* 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;
+	}
+
+	/* 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;	
+
+out_fail:
+	x = errno;
+	if (d_ids)
+		free(d_ids);
+	if (vl)
+		free(vl);
+	errno = x;
+	return NULL;
+}
+
+
+/* Returns 0 if equal, nonzero if not */
+int
+vl_cmp(virt_list_t *left, virt_list_t *right)
+{
+	int x;
+
+	/* Quick checks */
+	if (!left->vm_count && !right->vm_count)
+		return 1;
+	if (left->vm_count != right->vm_count)
+		return 0;
+
+	for (x = 0; x < left->vm_count; x++) {
+		if (strcmp(left->vm_states[x].v_name,
+			   right->vm_states[x].v_name))
+			return 1;
+		/*
+		if (left->vm_states[x].v_state.s_state !=
+		    right->vm_states[x].v_state.s_state)
+			return 1;
+		 */
+	}
+
+	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, char *name)
+{
+	int b,e,x,y;
+
+	if (!vl || !name || !vl->vm_count)
+		return NULL;
+
+	b = 0;
+	e = vl->vm_count;
+
+	do {
+		x = (b + e) / 2;
+		y = strcasecmp(vl->vm_states[x].v_name, name);
+		if (y == 0) {
+			return &vl->vm_states[x];
+		} else if (y < 0) {
+			e = x;
+		} else {
+			b = x;
+		}
+	} while (b != e);
+
+	return NULL;
+}
+
+
+virt_state_t *
+vl_find_uuid(virt_list_t *vl, 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);
+}
/cvs/cluster/cluster/fence/agents/xvm/virt.h,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/virt.h
+++ -	2006-10-05 16:11:41.035469000 +0000
@@ -0,0 +1,80 @@
+/*
+  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 _VIRT_H
+#define _VIRT_H
+#include <libvirt.h>
+#include <stdint.h>
+#include <netinet/in.h>
+
+#include "xvm.h"
+
+/*
+   Owner 0 = no owner.
+
+   checkpoint "xen-vm-states" {
+     section "vm-name0" {
+       owner_nodeid;
+       vm_state;
+     }
+     section "vm-name1" {
+       owner_nodeid;
+       vm_state;
+     }
+     ...
+   }
+ */
+
+typedef struct {
+	uint32_t s_owner;
+	int32_t s_state;
+} vm_state_t;
+
+typedef struct {
+	char v_name[MAX_DOMAINNAME_LENGTH];
+	char v_uuid[MAX_DOMAINNAME_LENGTH];
+	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 my_id);
+
+int vl_cmp(virt_list_t *left, virt_list_t *right);
+
+void vl_print(virt_list_t *vl);
+void vl_free(virt_list_t *old);
+virt_state_t * vl_find_uuid(virt_list_t *vl, char *name);
+virt_state_t * vl_find_name(virt_list_t *vl, char *name);
+
+
+typedef void ckpt_handle;
+int ckpt_read(void *hp, char *secid, void *buf, size_t maxlen);
+int ckpt_finish(void *hp);
+int ckpt_write(void *hp, char *secid, void *buf, size_t maxlen);
+void *ckpt_init(char *ckpt_name, int maxlen, int maxsec, int maxseclen,
+		int timeout);
+
+
+#endif
/cvs/cluster/cluster/fence/agents/xvm/vm_states.c,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/vm_states.c
+++ -	2006-10-05 16:11:41.160348000 +0000
@@ -0,0 +1,427 @@
+/*
+  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.
+*/
+/** @file
+ * Distributed VM states using saCkpt interface
+ */
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <saAis.h>
+#include <saCkpt.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+typedef struct {
+	uint32_t ck_ready;
+	int ck_timeout;
+	SaCkptCheckpointHandleT ck_checkpoint;
+	SaCkptHandleT ck_handle;
+	char *ck_name;
+} ckpt_handle;
+
+
+#define READY_MAGIC 0x13fd237c
+#define VALIDATE(h) \
+do { \
+	if (!h || h->ck_ready != READY_MAGIC) { \
+		errno = EINVAL; \
+		return -1; \
+	} \
+} while(0)
+
+int ais_to_posix(SaAisErrorT err);
+
+int
+ais_to_posix(SaAisErrorT err)
+{
+	switch (err) {
+	case SA_AIS_OK:
+		return 0;
+	case SA_AIS_ERR_LIBRARY:
+		return ELIBBAD;
+	case SA_AIS_ERR_VERSION:
+		return EPROTONOSUPPORT; //XXX
+	case SA_AIS_ERR_INIT:
+		return EFAULT; //XXX
+	case SA_AIS_ERR_TIMEOUT:
+		return ETIMEDOUT;
+	case SA_AIS_ERR_TRY_AGAIN:
+		return EAGAIN;
+	case SA_AIS_ERR_INVALID_PARAM:
+		return EINVAL;
+	case SA_AIS_ERR_NO_MEMORY:
+		return ENOMEM;
+	case SA_AIS_ERR_BAD_HANDLE:
+		return EBADF;
+	case SA_AIS_ERR_BUSY:
+		return EBUSY;
+	case SA_AIS_ERR_ACCESS:
+		return EACCES;
+	case SA_AIS_ERR_NOT_EXIST:
+		return ENOENT;
+	case SA_AIS_ERR_NAME_TOO_LONG:
+		return ENAMETOOLONG;
+	case SA_AIS_ERR_EXIST:
+		return EEXIST;
+	case SA_AIS_ERR_NO_SPACE:
+		return ENOSPC;
+	case SA_AIS_ERR_INTERRUPT:
+		return EINTR;
+	case SA_AIS_ERR_NAME_NOT_FOUND:
+		return ENOENT;
+	case SA_AIS_ERR_NO_RESOURCES:
+		return ENOMEM; //XXX
+	case SA_AIS_ERR_NOT_SUPPORTED:
+		return ENOSYS;
+	case SA_AIS_ERR_BAD_OPERATION:
+		return EINVAL; //XXX
+	case SA_AIS_ERR_FAILED_OPERATION:
+		return EIO; //XXX
+	case SA_AIS_ERR_MESSAGE_ERROR:
+		return EIO; // XXX
+	case SA_AIS_ERR_QUEUE_FULL:
+		return ENOBUFS;
+	case SA_AIS_ERR_QUEUE_NOT_AVAILABLE:
+		return ENOENT;
+	case SA_AIS_ERR_BAD_FLAGS:
+		return EINVAL;
+	case SA_AIS_ERR_TOO_BIG:
+		return E2BIG;
+	case SA_AIS_ERR_NO_SECTIONS:
+		return ENOENT; // XXX
+	}
+
+	return -1;
+}
+
+
+static int 
+ckpt_open(ckpt_handle *h, const char *ckpt_name, int maxsize,
+	  int maxsec, int maxsecsize, int timeout)
+{
+	SaCkptCheckpointCreationAttributesT attrs;
+	SaCkptCheckpointOpenFlagsT flags;
+	SaNameT cpname;
+#if 0
+	SaCkptCheckpointDescriptorT status;
+#endif
+	SaAisErrorT err = SA_AIS_OK;
+
+	VALIDATE(h);
+
+	flags = SA_CKPT_CHECKPOINT_READ |
+		SA_CKPT_CHECKPOINT_WRITE;
+
+	snprintf((char *)cpname.value, SA_MAX_NAME_LENGTH-1,
+		 "%s", ckpt_name);
+	cpname.length = strlen(ckpt_name);
+
+	h->ck_timeout = timeout;
+
+	err = saCkptCheckpointOpen(h->ck_handle,
+				   &cpname,
+				   NULL,	
+				   flags,
+				   timeout,
+				   &h->ck_checkpoint);
+
+	if (err == SA_AIS_OK) {
+#if 0
+		saCkptCheckpointStatusGet(h->ck_handle,
+					  &status);
+
+		printf("Checkpoint Size = %d bytes\n", (int)
+			status.checkpointCreationAttributes.checkpointSize);
+		printf("Flags = ");
+		if (status.checkpointCreationAttributes.creationFlags &
+			SA_CKPT_WR_ALL_REPLICAS) {
+			printf("%s ", "SA_CKPT_WR_ALL_REPLICAS");
+		}
+		if (status.checkpointCreationAttributes.creationFlags &
+			SA_CKPT_WR_ACTIVE_REPLICA) {
+			printf("%s ", "SA_CKPT_WR_ACTIVE_REPLICA");
+		}
+		if (status.checkpointCreationAttributes.creationFlags &
+			SA_CKPT_WR_ACTIVE_REPLICA_WEAK) {
+			printf("%s ", "SA_CKPT_WR_ACTIVE_REPLICA_WEAK");
+		}
+		if (status.checkpointCreationAttributes.creationFlags &
+			SA_CKPT_CHECKPOINT_COLLOCATED) {
+			printf("%s ", "SA_CKPT_CHECKPOINT_COLLOCATED");
+		}
+		printf("\nMax sections = %d\n",
+			(int)status.checkpointCreationAttributes.maxSections);
+		printf("Max section size = %d\n",
+			(int)status.checkpointCreationAttributes.maxSectionSize);
+		printf("Max section ID size = %d\n",
+			(int)status.checkpointCreationAttributes.maxSectionIdSize);
+		printf("Section count = %d\n", status.numberOfSections);
+		printf("\n");
+#endif
+		goto good;
+	}
+
+	attrs.creationFlags = SA_CKPT_WR_ALL_REPLICAS;
+	attrs.checkpointSize = (SaSizeT)maxsize;
+	attrs.retentionDuration = SA_TIME_ONE_HOUR;
+	attrs.maxSections = maxsec;
+	attrs.maxSectionSize = (SaSizeT)maxsecsize;
+	attrs.maxSectionIdSize = (SaSizeT)32;
+
+	flags = SA_CKPT_CHECKPOINT_READ |
+		SA_CKPT_CHECKPOINT_WRITE |
+		SA_CKPT_CHECKPOINT_CREATE;
+
+	err = saCkptCheckpointOpen(h->ck_handle,
+				   &cpname,
+				   &attrs,
+				   flags,
+				   timeout,
+				   &h->ck_checkpoint);
+	if (err == SA_AIS_OK)
+		goto good;
+
+	/* No checkpoint */
+	errno = ais_to_posix(err);
+	return (errno == 0 ? 0 : -1);
+good:
+	printf("Opened ckpt %s\n", ckpt_name);
+	h->ck_name = strdup(ckpt_name);
+
+	errno = ais_to_posix(err);
+	return (errno == 0 ? 0 : -1);
+}
+
+
+void *
+ckpt_init(char *ckpt_name, int maxlen, int maxsec,
+	  int maxseclen, int timeout)
+{
+	ckpt_handle *h;
+	SaAisErrorT err;
+	SaVersionT ver;
+
+	if (!ckpt_name || !strlen(ckpt_name)) {
+		errno = EINVAL;
+		return NULL;
+	}
+	h = malloc(sizeof(*h));
+	if (!h)
+		return NULL;
+	memset(h, 0, sizeof(*h));
+
+	ver.releaseCode = 'B';
+	ver.majorVersion = 1;
+	ver.minorVersion = 1;
+
+	err = saCkptInitialize(&h->ck_handle, NULL, &ver);
+
+	if (err != SA_AIS_OK)
+		free(h);
+	else
+		h->ck_ready = READY_MAGIC;
+
+	if (ckpt_open(h, ckpt_name, maxlen, maxsec, maxseclen,
+		      timeout) < 0) {
+		saCkptCheckpointClose(h->ck_checkpoint);
+		if (h->ck_name) 
+			free(h->ck_name);
+		free(h);
+		return NULL;
+	}
+
+	return (void *)h;
+}
+
+
+int
+ckpt_write(void *hp, char *secid, void *buf, size_t maxlen)
+{
+	ckpt_handle *h = (ckpt_handle *)hp;
+	SaCkptIOVectorElementT iov = {SA_CKPT_DEFAULT_SECTION_ID,
+				      NULL, 0, 0, 0};
+	SaAisErrorT err;
+	SaCkptSectionCreationAttributesT attrs;
+
+	VALIDATE(h);
+
+	/* Set section ID here */
+	iov.sectionId.id = (uint8_t *)secid;
+	iov.sectionId.idLen = strlen(secid);
+	iov.dataBuffer = buf;
+	iov.dataSize = (SaSizeT)maxlen;
+	iov.dataOffset = 0;
+	iov.readSize = 0;
+
+	err = saCkptCheckpointWrite(h->ck_checkpoint, &iov, 1, NULL);
+
+	if (err == SA_AIS_ERR_NOT_EXIST) {
+		attrs.sectionId = &iov.sectionId;
+		attrs.expirationTime = SA_TIME_END;
+
+		err = saCkptSectionCreate(h->ck_checkpoint, &attrs, 
+					  buf, maxlen);
+	}
+
+	if (err == SA_AIS_OK)
+		saCkptCheckpointSynchronize(h->ck_checkpoint,
+					    h->ck_timeout);
+
+	errno = ais_to_posix(err);
+	if (errno)
+		return -1;
+	return maxlen; /* XXX */
+}
+
+
+int
+ckpt_read(void *hp, char *secid, void *buf, size_t maxlen)
+{
+	ckpt_handle *h = (ckpt_handle *)hp;
+	SaCkptIOVectorElementT iov = {SA_CKPT_DEFAULT_SECTION_ID,
+				      NULL, 0, 0, 0};
+	SaAisErrorT err;
+
+	VALIDATE(h);
+	//printf("reading ckpt %s\n", keyid);
+
+	iov.sectionId.id = (uint8_t *)secid;
+	iov.sectionId.idLen = strlen(secid);
+	iov.dataBuffer = buf;
+	iov.dataSize = (SaSizeT)maxlen;
+	iov.dataOffset = 0;
+	iov.readSize = 0;
+
+	err = saCkptCheckpointRead(h->ck_checkpoint, &iov, 1, NULL);
+
+	errno = ais_to_posix(err);
+	if (errno)
+		return -1;
+	return iov.readSize; /* XXX */
+}
+
+
+int
+ckpt_finish(void *hp)
+{
+	ckpt_handle *h = (ckpt_handle *)hp;
+	int ret = 0;
+	SaAisErrorT err;
+
+	saCkptCheckpointClose(h->ck_checkpoint);
+	err = saCkptFinalize(h->ck_handle);
+
+	if (err != SA_AIS_OK)
+		ret = -1;
+	else
+		h->ck_ready = 0;
+
+	if (h->ck_name)
+		free(h->ck_name);
+
+	if (ret != 0)
+		errno = ais_to_posix(err);
+	return ret;
+}
+
+
+#ifdef STANDALONE
+void
+usage(int ret)
+{
+	printf("usage: ckpt [-c ckpt_name] <-r key|-w key -d data>\n");
+	exit(ret);
+}
+
+int
+main(int argc, char **argv)
+{
+	char *ckptname = "ckpt_test";
+	char *sec = "default";
+	char *val;
+	void *h;
+	char buf[64];
+	int ret;
+	int op = 0;
+
+	while((ret = getopt(argc, argv, "c:w:r:d:j?")) != EOF) {
+		switch(ret) {
+		case 'c': 
+			ckptname = optarg;
+			break;
+		case 'w': 
+			op = 'w';
+			sec = optarg;
+			break;
+		case 'r':
+			op = 'r';
+			sec = optarg;
+			break;
+		case 'd':
+			val = optarg;
+			break;
+		case '?':
+		case 'h':
+			usage(0);
+		default:
+			usage(1);
+		}
+	}
+
+	if (!op) {
+		usage(1);
+	}
+
+	if (!sec) {
+		usage(1);
+	}
+
+	h = ckpt_init(ckptname, 262144, 4096, 64, 10);
+	if (!h) {
+		perror("ckpt_init");
+		return -1;
+	}
+
+	if (op == 'w') {
+		if (ckpt_write(h, sec, val, strlen(val)+1) < 0) {
+			perror("ckpt_write");
+			return 1;
+		}
+	} else if (op == 'r') {
+		ret = ckpt_read(h, sec, buf, sizeof(buf));
+		if (ret < 0) {
+			perror("ckpt_read");
+			return 1;
+		}
+
+		printf("%d bytes\nDATA for '%s':\n%s\n", ret, sec,
+		       buf);
+	}
+
+	ckpt_finish(h);
+
+	return 0;
+}
+#endif
/cvs/cluster/cluster/fence/agents/xvm/xvm.h,v  -->  standard output
revision 1.1
--- cluster/fence/agents/xvm/xvm.h
+++ -	2006-10-05 16:11:41.255250000 +0000
@@ -0,0 +1,75 @@
+/*
+  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>
+
+#define XVM_VERSION "0.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_cmd_t;
+
+#define MAX_HASH_LENGTH SHA512_LENGTH
+
+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 */
+	uint16_t port;			/* Port we bound to */
+	uint8_t  random[10];		/* Random Data */
+	uint32_t family;		/* Address family */
+	uint8_t  hash[MAX_HASH_LENGTH];	/* Binary hash */
+} fence_req_t;
+
+#endif
/cvs/cluster/cluster/fence/man/fence_xvm.8,v  -->  standard output
revision 1.1
--- cluster/fence/man/fence_xvm.8
+++ -	2006-10-05 16:11:41.334733000 +0000
@@ -0,0 +1,139 @@
+.\"  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+.\"  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+.\"  
+.\"  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.
+
+.TH fence_xvm 8
+
+.SH NAME
+fence_xvm - I/O Fencing agent for Xen virtual machines.
+
+.SH SYNOPSIS
+.B
+fence_xvm
+[\fIOPTION\fR]...
+
+.SH DESCRIPTION
+fence_xvm is an I/O Fencing agent which can be used with Xen virtual machines
+which are part of a cluster.  There is a requirement that the parent 
+domain-0s are also a part of a CMAN/OpenAIS based cluster, such as
+linux-cluster.
+
+fence_xvm accepts options on the command line as well as from standard input.  
+If no command line options are present, fence_xvm will automatically use
+standard input.  fenced sends the options through stdin when it execs the
+agent.  fence_xvm can be run by itself with command line options which is
+useful for testing.
+
+.SH OPTIONS
+.TP
+\fB-d\fP
+Enable debugging output
+.TP
+\fB-i\fP \fIfamily\fP
+IP family to use (auto, ipv4, or ipv6; default = auto)
+.TP
+\fB-a\fP \fIaddress\fP
+Multicast address to use (default=225.0.0.12 for ipv4, ff02::3:1 for ipv6)
+.TP
+\fB-p\fP \fIport\fP
+Port to use for all communications; must match fence_xvmd (default=1229)
+.TP
+\fB-r\fP \fIretrans\fP
+Multicast retransmission time (in 1/10 seconds; default=20).  This
+is used to tune the amount of retransmission which is done on busy networks
+where multicast traffic is unreliable.
+.TP
+\fB-C\fP \fIauth\fP
+Authentication type (none, sha1, sha256, sha512; default=sha256).  This
+controls the authentication mechanism used to authenticate clients.  The
+three SHA hashes use a key which must be shared between both the Xen virtual
+machines and the host domain-0 cluster.  The three SHA authentication 
+mechanisms use a simple bidirectional challenge-response based on pseudo-
+random number generation and a shared private key.
+.TP
+\fB-c\fP \fIhash\fP
+Packet hash type (none, sha1, sha256, sha512; default=sha256).  This
+controls the hashing mechanism used to authenticate fencing requests.  The
+three SHA hashes use a key which must be shared between both the Xen virtual
+machines and the host domain-0 cluster.
+.TP
+\fB-k\fP \fIkey_file\fP
+Use the specified key file for packet hashing / SHA authentication.
+.TP
+\fB-H\fP \fIdomain\fP
+This specifies unique domain name of the Xen guest which needs to be fenced.
+.TP
+\fB-u\fP
+Fence by UUID instead of Xen Domain name.  If specified, the above -H
+parameter should be the statically-defined 36-character UUID of the Xen
+domain rather than its human-readable name.
+.TP
+\fB-o\fP \fIoperation\fP
+Fencing operation to perform (null, off, reboot; default=off).  This specifies
+the fencing operation to perform.  The null operation does not actually do
+anything, but allows you to check to see if communication with fence_xvmd
+is working correctly (note: the result of the null operation is *always*
+failure!).
+.TP
+\fB-t\fP \fItimeout\fP
+This specifies the amount of time, in seconds, to wait before a response to
+the multicast request before giving up (default=30).
+.TP
+\fB-?\fP
+Print out a help message describing available options, then exit.
+.TP
+\fB-h\fP
+Print out a help message describing available options, then exit.
+.TP
+\fB-V\fP
+Print out a version message, then exit.
+
+.SH STDIN PARAMETERS
+.TP
+\fIdebug = 1\fR
+Same as the -d option.
+.TP
+\fIfamily = < param >\fR
+Same as the -i option.
+.TP
+\fImulticast_address = < param >\fR
+Same as the -a option.
+.TP
+\fIport = < param >\fR
+Same as the -p option.
+.TP
+\fIretrans = < param >\fR
+Same as the -r option.
+.TP
+\fIauth = < param >\fR
+Same as the -C option.
+.TP
+\fIhash = < param >\fR
+Same as the -c option.
+.TP
+\fIkey_file = < param >\fR
+Same as the -k option.
+.TP
+\fIretrans = < param >\fR
+Same as the -r option.
+.TP
+\fIdomain = < param >\fR
+Same as the -H option.
+.TP
+\fIuse_uuid = 1\fR
+Same as the -u option.
+.TP
+\fIoption = < param >\fR
+Same as the -o option.
+.TP
+\fItimeout = < param >\fR
+Same as the -t option.
+.TP
+\fIdomain = < param >\fR
+Same as the -H option.
+
+.SH SEE ALSO
+fence(8), fence_node(8), fence_xvmd(8)
/cvs/cluster/cluster/fence/man/fence_xvmd.8,v  -->  standard output
revision 1.1
--- cluster/fence/man/fence_xvmd.8
+++ -	2006-10-05 16:11:41.413544000 +0000
@@ -0,0 +1,78 @@
+.\"  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+.\"  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+.\"  
+.\"  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.
+
+.TH fence_xvmd 8
+
+.SH NAME
+fence_xvmd - I/O Fencing agent host for Xen virtual machines.
+
+.SH SYNOPSIS
+.B
+fence_xvmd
+[\fIOPTION\fR]...
+
+.SH DESCRIPTION
+fence_xvmd is an I/O Fencing host which resides on dom0 and is used in
+conjunction with the fence_xvm fencing agent.  Together, these two programs
+can be used to fence can be used to fence Xen virtual machines which are
+part of a cluster.  There is a requirement that the parent 
+dom0s are also a part of their own CMAN/OpenAIS based cluster, and that
+the dom0 cluster does not share any members with the domU cluster.
+Furthermore, the dom0 cluster is required to have fencing if domU recovery
+is expected to be automatic.
+
+fence_xvmd accepts options on the command line.
+
+.SH OPTIONS
+.TP
+\fB-f\fP
+Foreground mode (do not fork)
+.TP
+\fB-d\fP
+Enable debugging output
+.TP
+\fB-i\fP \fIfamily\fP
+IP family to use (auto, ipv4, or ipv6; default = auto)
+.TP
+\fB-a\fP \fIaddress\fP
+Multicast address to listen on (default=225.0.0.12 for ipv4, ff02::3:1
+for ipv6)
+.TP
+\fB-p\fP \fIport\fP
+Port to use (default=1229)
+.TP
+\fB-C\fP \fIauth\fP
+Authentication type (none, sha1, sha256, sha512; default=sha256).  This
+controls the authentication mechanism used to authenticate clients.  The
+three SHA hashes use a key which must be shared between both the Xen virtual
+machines and the host domain-0 cluster.  The three SHA authentication 
+mechanisms use a simple bidirectional challenge-response based on pseudo-
+random number generation and a shared private key.
+.TP
+\fB-c\fP \fIhash\fP
+Packet hash type (none, sha1, sha256, sha512; default=sha256).  This
+controls the hashing mechanism used to authenticate fencing requests.  The
+three SHA hashes use a key which must be shared between both the Xen virtual
+machines and the host domain-0 cluster.
+.TP
+\fB-k\fP \fIkey_file\fP
+Use the specified key file for packet hashing / SHA authentication.
+.TP
+\fB-u\fP
+Fence by UUID instead of Xen Domain name.
+.TP
+\fB-?\fP
+Print out a help message describing available options, then exit.
+.TP
+\fB-h\fP
+Print out a help message describing available options, then exit.
+.TP
+\fB-V\fP
+Print out a version message, then exit.
+
+.SH SEE ALSO
+fence(8), fence_node(8), fence_xvm(8)
--- cluster/fence/man/Makefile	2006/02/08 15:51:33	1.9
+++ cluster/fence/man/Makefile	2006/10/05 16:11:36	1.10
@@ -31,6 +31,8 @@
 	fence_wti.8 \
 	fence_bullpap.8 \
 	fence_ipmilan.8 \
+	fence_xvm.8 \
+	fence_xvmd.8 \
 	fenced.8
 
 UNINSTALL=${top_srcdir}/scripts/uninstall.pl




More information about the Cluster-devel mailing list