summaryrefslogtreecommitdiffstats
path: root/agents/virt/server
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:50:17 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:50:17 +0000
commit86ed03f8adee56c050c73018537371c230a664a6 (patch)
treeeae3d04cdf1c49848e5a671327ab38297f4acb0d /agents/virt/server
parentInitial commit. (diff)
downloadfence-agents-86ed03f8adee56c050c73018537371c230a664a6.tar.xz
fence-agents-86ed03f8adee56c050c73018537371c230a664a6.zip
Adding upstream version 4.12.1.upstream/4.12.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--agents/virt/server/Makefile.am79
-rw-r--r--agents/virt/server/config.c698
-rw-r--r--agents/virt/server/cpg-virt.c643
-rw-r--r--agents/virt/server/cpg.c411
-rw-r--r--agents/virt/server/cpg.h29
-rw-r--r--agents/virt/server/daemon_init.c215
-rw-r--r--agents/virt/server/history.c124
-rw-r--r--agents/virt/server/libvirt.c359
-rw-r--r--agents/virt/server/main.c281
-rw-r--r--agents/virt/server/mcast.c622
-rw-r--r--agents/virt/server/plugin.c417
-rw-r--r--agents/virt/server/serial.c459
-rw-r--r--agents/virt/server/serial.h20
-rw-r--r--agents/virt/server/static_map.c237
-rw-r--r--agents/virt/server/tcp.c575
-rw-r--r--agents/virt/server/uuid-test.c66
-rw-r--r--agents/virt/server/uuid-test.h14
-rw-r--r--agents/virt/server/virt-serial.c444
-rw-r--r--agents/virt/server/virt-sockets.c242
-rw-r--r--agents/virt/server/virt.c630
-rw-r--r--agents/virt/server/virt.h62
-rw-r--r--agents/virt/server/vsock.c565
22 files changed, 7192 insertions, 0 deletions
diff --git a/agents/virt/server/Makefile.am b/agents/virt/server/Makefile.am
new file mode 100644
index 0000000..fbca617
--- /dev/null
+++ b/agents/virt/server/Makefile.am
@@ -0,0 +1,79 @@
+###############################################################################
+###############################################################################
+##
+## Copyright (C) 2009-2021 Red Hat, Inc.
+##
+## This copyrighted material is made available to anyone wishing to use,
+## modify, copy, or redistribute it subject to the terms and conditions
+## of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+MAINTAINERCLEANFILES = Makefile.in
+
+noinst_HEADERS = cpg.h serial.h uuid-test.h virt.h
+
+sbin_PROGRAMS = fence_virtd
+
+#
+# daemon
+#
+fence_virtd_SOURCES = main.c plugin.c config.c static_map.c uuid-test.c \
+ daemon_init.c
+
+fence_virtd_CFLAGS = $(VIRT_AM_CFLAGS) \
+ $(nss_CFLAGS) $(xml2_CFLAGS) $(uuid_CFLAGS) $(PTHREAD_CFLAGS) \
+ $(AM_CFLAGS)
+
+fence_virtd_LDADD = $(VIRT_CONFIG_LIBS) $(VIRT_COMMON_LIBS) \
+ $(nss_LIBS) $(xml2_LIBS) $(uuid_LIBS) $(PTHREAD_LIBS) $(dl_LIBS)
+
+fence_virtd_LDFLAGS = $(VIRT_AM_LDFLAGS) $(VIRT_COMMON_LDFLAGS)
+
+virt_la_SOURCES = libvirt.c virt.c uuid-test.c
+cpg_la_SOURCES = cpg-virt.c cpg.c virt.c uuid-test.c
+multicast_la_SOURCES = mcast.c history.c
+tcp_la_SOURCES = tcp.c history.c
+vsock_la_SOURCES = vsock.c history.c
+serial_la_SOURCES = virt-serial.c virt-sockets.c serial.c history.c
+
+fence_virtd_CFLAGS += -DMODULE_PATH=\"$(libdir)/fence-virt/\"
+
+fvlibdir = $(libdir)/fence-virt
+
+fvlib_LTLIBRARIES =
+
+MODULESCFLAGS = $(VIRT_AM_CFLAGS) $(AM_CFLAGS)
+MODULESLDFLAGS = $(VIRT_AM_LDFLAGS) $(VIRT_COMMON_LIBS) $(VIRT_COMMON_LDFLAGS) -module -avoid-version -export-dynamic
+
+if modlibvirt
+fvlib_LTLIBRARIES += virt.la
+virt_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(virt_CFLAGS)
+virt_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(virt_LIBS)
+endif
+if modcpg
+fvlib_LTLIBRARIES += cpg.la
+cpg_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(cpg_CFLAGS) $(virt_CFLAGS)
+cpg_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(cpg_LIBS) $(virt_LIBS)
+endif
+if modmulticast
+fvlib_LTLIBRARIES += multicast.la
+multicast_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS)
+multicast_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS)
+endif
+if modserial
+fvlib_LTLIBRARIES += serial.la
+serial_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(xml2_CFLAGS) $(virt_CFLAGS)
+serial_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(xml2_LIBS) $(virt_LIBS)
+endif
+if modtcp
+fvlib_LTLIBRARIES += tcp.la
+tcp_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS)
+tcp_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS)
+endif
+if modvsock
+fvlib_LTLIBRARIES += vsock.la
+vsock_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS)
+vsock_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS)
+endif
diff --git a/agents/virt/server/config.c b/agents/virt/server/config.c
new file mode 100644
index 0000000..fa9af97
--- /dev/null
+++ b/agents/virt/server/config.c
@@ -0,0 +1,698 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "mcast.h"
+#include "xvm.h"
+#include "server_plugin.h"
+#include "simple_auth.h"
+
+
+static int
+yesno(const char *prompt, int dfl)
+{
+ char result[10];
+
+ printf("%s [%c/%c]? ", prompt, dfl?'Y':'y', dfl?'n':'N');
+ fflush(stdout);
+
+ memset(result, 0, sizeof(result));
+ if (fgets(result, 9, stdin) == NULL)
+ return dfl;
+
+ if (result[0] == 'y' || result[0] == 'Y')
+ return 1;
+ if (result[0] == 'n' || result[0] == 'N')
+ return 0;
+
+ return dfl;
+}
+
+
+static int
+text_input(const char *prompt, const char *dfl, char *input, size_t len)
+{
+ const char *tmpdfl = dfl;
+ const char *nulldfl = "";
+
+ if (dfl == NULL) {
+ tmpdfl = nulldfl;
+ }
+
+ printf("%s [%s]: ", prompt, tmpdfl);
+ fflush(stdout);
+
+ memset(input, 0, len);
+ if (fgets(input, len, stdin) == NULL) {
+ strncpy(input, tmpdfl, len);
+ return 0;
+ }
+ if (input[strlen(input)-1] == '\n')
+ input[strlen(input)-1] = 0;
+
+ if (strlen(input) == 0) {
+ strncpy(input, tmpdfl, len);
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int
+plugin_path_configure(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done = 0;
+
+ if (sc_get(config, "fence_virtd/@module_path", val,
+ sizeof(val))) {
+#ifdef MODULE_PATH
+ snprintf(val, sizeof(val), MODULE_PATH);
+#else
+ printf("Failed to determine module search path.\n");
+#endif
+ }
+
+ do {
+ text_input("Module search path", val, inp, sizeof(inp));
+
+ printf("\n");
+ done = plugin_search(inp);
+ if (done > 0) {
+ plugin_dump();
+ done = 1;
+ } else {
+ done = 0;
+ printf("No modules found in %s!\n", inp);
+ if (yesno("Use this value anyway", 0) == 1)
+ done = 1;
+ }
+ } while (!done);
+
+ sc_set(config, "fence_virtd/@module_path", inp);
+
+ return 0;
+}
+
+
+static int
+backend_config_libvirt(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+
+ printf("\n");
+ printf("The libvirt backend module is designed for single desktops or\n"
+ "servers. Do not use in environments where virtual machines\n"
+ "may be migrated between hosts.\n\n");
+
+ /* Default backend plugin */
+ if (sc_get(config, "backends/libvirt/@uri", val,
+ sizeof(val))) {
+ strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
+ }
+
+ text_input("Libvirt URI", val, inp, sizeof(inp));
+
+ sc_set(config, "backends/libvirt/@uri", inp);
+
+ return 0;
+}
+
+
+static int
+backend_config_cpg(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done = 0;
+
+ printf("\n");
+ printf("The CPG backend module is designed for use in clusters\n"
+ "running corosync and libvirt. It utilizes the CPG API to \n"
+ "route fencing requests, finally utilizing libvirt to perform\n"
+ "fencing actions.\n\n");
+
+ if (sc_get(config, "backends/cpg/@uri", val,
+ sizeof(val))) {
+ strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
+ }
+
+ text_input("Libvirt URI", val, inp, sizeof(inp));
+
+ sc_set(config, "backends/cpg/@uri", inp);
+
+ printf("\n");
+ printf("The name mode is how the cpg plugin stores and \n"
+ "references virtual machines. Since virtual machine names\n"
+ "are not guaranteed to be unique cluster-wide, use of UUIDs\n"
+ "is strongly recommended. However, for compatibility with \n"
+ "fence_xvmd, the use of 'name' mode is also supported.\n\n");
+
+ if (sc_get(config, "backends/cpg/@name_mode", val,
+ sizeof(val))) {
+ strncpy(val, "uuid", sizeof(val));
+ }
+
+ do {
+ text_input("VM naming/tracking mode (name or uuid)",
+ val, inp, sizeof(inp));
+ if (!strcasecmp(inp, "uuid")) {
+ done = 1;
+ } else if (!strcasecmp(inp, "name")) {
+ done = 0;
+ printf("This can be dangerous if you do not take care to"
+ "ensure that\n"
+ "virtual machine names are unique "
+ "cluster-wide.\n");
+ if (yesno("Use name mode anyway", 1) == 1)
+ done = 1;
+ }
+ } while (!done);
+
+ sc_set(config, "backends/cpg/@name_mode", inp);
+
+ return 0;
+}
+
+
+static int
+listener_config_multicast(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ const char *family = "ipv4";
+ struct in_addr sin;
+ struct in6_addr sin6;
+ int done = 0;
+
+ printf("\n");
+ printf("The multicast listener module is designed for use environments\n"
+ "where the guests and hosts may communicate over a network using\n"
+ "multicast.\n\n");
+
+
+ /* MULTICAST IP ADDRESS/FAMILY */
+ printf("The multicast address is the address that a client will use to\n"
+ "send fencing requests to fence_virtd.\n\n");
+
+ if (sc_get(config, "listeners/multicast/@address",
+ val, sizeof(val)-1)) {
+ strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Multicast IP Address", val, inp, sizeof(inp));
+
+ if (inet_pton(AF_INET, inp, &sin) == 1) {
+ printf("\nUsing ipv4 as family.\n\n");
+ family = "ipv4";
+ done = 1;
+ } else if (inet_pton(AF_INET6, inp, &sin6) == 1) {
+ printf("\nUsing ipv6 as family.\n\n");
+ family = "ipv6";
+ done = 1;
+ } else
+ printf("'%s' is not a valid IP address!\n", inp);
+ } while (!done);
+
+ sc_set(config, "listeners/multicast/@family", family);
+ sc_set(config, "listeners/multicast/@address", inp);
+
+ /* MULTICAST IP PORT */
+ if (sc_get(config, "listeners/multicast/@port",
+ val, sizeof(val)-1)) {
+ snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT);
+ }
+
+ done = 0;
+ do {
+ char *p;
+ int ret;
+
+ text_input("Multicast IP Port", val, inp, sizeof(inp));
+ ret = strtol(inp, &p, 0);
+ if (*p != '\0' || ret <= 0 || ret >= 65536) {
+ printf("Port value '%s' is out of range\n", val);
+ continue;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "listeners/multicast/@port", inp);
+
+ /* MULTICAST INTERFACE */
+ printf("\nSetting a preferred interface causes fence_virtd to listen only\n"
+ "on that interface. Normally, it listens on all interfaces.\n"
+ "In environments where the virtual machines are using the host\n"
+ "machine as a gateway, this *must* be set (typically to virbr0).\n"
+ "Set to 'none' for no interface.\n\n"
+ );
+
+ if (sc_get(config, "listeners/multicast/@interface",
+ val, sizeof(val)-1)) {
+ strncpy(val, "none", sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Interface", val, inp, sizeof(inp));
+
+ if (!strcasecmp(inp, "none")) {
+ break;
+ }
+
+ if (strlen(inp) > 0) {
+ int ret;
+
+ ret = if_nametoindex(inp);
+ if (ret < 0) {
+ printf("Invalid interface: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } else
+ printf("No interface given\n");
+ } while (!done);
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/multicast/@interface", NULL);
+ } else {
+ sc_set(config, "listeners/multicast/@interface", inp);
+ }
+
+
+ /* KEY FILE */
+ printf("\nThe key file is the shared key information which is used to\n"
+ "authenticate fencing requests. The contents of this file must\n"
+ "be distributed to each physical host and virtual machine within\n"
+ "a cluster.\n\n");
+
+ if (sc_get(config, "listeners/multicast/@key_file",
+ val, sizeof(val)-1)) {
+ strncpy(val, DEFAULT_KEY_FILE, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Key File", val, inp, sizeof(inp));
+
+ if (!strcasecmp(inp, "none")) {
+ break;
+ }
+
+ if (strlen(inp) > 0) {
+ if (inp[0] != '/') {
+ printf("Invalid key file: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } else
+ printf("No key file given\n");
+ } while (!done);
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/multicast/@key_file", NULL);
+ } else {
+ sc_set(config, "listeners/multicast/@key_file", inp);
+ }
+
+ return 0;
+}
+
+static int
+listener_config_tcp(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ const char *family = "ipv4";
+ struct in_addr sin;
+ struct in6_addr sin6;
+ int done = 0;
+
+ printf("\n");
+ printf("The TCP listener module is designed for use in environments\n"
+ "where the guests and hosts communicate over viosproxy.\n\n");
+
+ /* IP ADDRESS/FAMILY */
+ printf("The IP address is the address that a client will use to\n"
+ "send fencing requests to fence_virtd.\n\n");
+
+ if (sc_get(config, "listeners/tcp/@address",
+ val, sizeof(val)-1)) {
+ strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("TCP Listen IP Address", val, inp, sizeof(inp));
+
+ if (inet_pton(AF_INET, inp, &sin) == 1) {
+ printf("\nUsing ipv4 as family.\n\n");
+ family = "ipv4";
+ done = 1;
+ } else if (inet_pton(AF_INET6, inp, &sin6) == 1) {
+ printf("\nUsing ipv6 as family.\n\n");
+ family = "ipv6";
+ done = 1;
+ } else {
+ printf("'%s' is not a valid IP address!\n", inp);
+ continue;
+ }
+ } while (!done);
+
+ sc_set(config, "listeners/tcp/@family", family);
+ sc_set(config, "listeners/tcp/@address", inp);
+
+ /* MULTICAST IP PORT */
+ if (sc_get(config, "listeners/tcp/@port",
+ val, sizeof(val)-1)) {
+ snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT);
+ }
+
+ done = 0;
+ do {
+ char *p;
+ int ret;
+
+ text_input("TCP Listen Port", val, inp, sizeof(inp));
+
+ ret = strtol(inp, &p, 0);
+ if (*p != '\0' || ret <= 0 || ret >= 65536) {
+ printf("Port value '%s' is out of range\n", val);
+ continue;
+ }
+ done = 1;
+ } while (!done);
+ sc_set(config, "listeners/tcp/@port", inp);
+
+ /* KEY FILE */
+ printf("\nThe key file is the shared key information which is used to\n"
+ "authenticate fencing requests. The contents of this file must\n"
+ "be distributed to each physical host and virtual machine within\n"
+ "a cluster.\n\n");
+
+ if (sc_get(config, "listeners/tcp/@key_file",
+ val, sizeof(val)-1)) {
+ strncpy(val, DEFAULT_KEY_FILE, sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Key File", val, inp, sizeof(inp));
+
+ if (!strcasecmp(inp, "none")) {
+ break;
+ }
+
+ if (strlen(inp) > 0) {
+ if (inp[0] != '/') {
+ printf("Invalid key file: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } else
+ printf("No key file given\n");
+ } while (!done);
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/tcp/@key_file", NULL);
+ } else {
+ sc_set(config, "listeners/tcp/@key_file", inp);
+ }
+
+ return 0;
+}
+
+static int
+listener_config_serial(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done;
+
+ printf("\n");
+ printf("The serial plugin allows fence_virtd to communicate with\n"
+ "guests using serial or guest-forwarding VMChannel instead\n"
+ "of using TCP/IP networking.\n\n");
+ printf("Special configuration of virtual machines is required. See\n"
+ "fence_virt.conf(5) for more details.\n\n");
+
+ if (sc_get(config, "listeners/serial/@uri",
+ val, sizeof(val)-1)) {
+ strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
+ }
+
+ text_input("Libvirt URI", val, inp, sizeof(inp));
+
+ printf("\nSetting a socket path prevents fence_virtd from taking\n"
+ "hold of all Unix domain sockets created when the guest\n"
+ "is started. A value like /var/run/cluster/fence might\n"
+ "be a good value. Don't forget to create the directory!\n\n");
+
+ if (sc_get(config, "listeners/serial/@path",
+ val, sizeof(val)-1)) {
+ strncpy(val, "none", sizeof(val));
+ }
+
+ text_input("Socket directory", val, inp, sizeof(inp));
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/serial/@path", NULL);
+ } else {
+ sc_set(config, "listeners/serial/@path", inp);
+ }
+
+ printf("\nThe serial plugin allows two types of guest to host\n"
+ "configurations. One is via a serial port; the other is\n"
+ "utilizing the newer VMChannel.\n\n");
+
+ if (sc_get(config, "listeners/serial/@mode",
+ val, sizeof(val)-1)) {
+ strncpy(val, "serial", sizeof(val));
+ }
+
+ if (!strcasecmp(inp, "none")) {
+ sc_set(config, "listeners/serial/@path", NULL);
+ } else {
+ sc_set(config, "listeners/serial/@path", inp);
+ }
+
+ done = 0;
+ do {
+ text_input("Mode (serial or vmchannel)", val, inp,
+ sizeof(inp));
+
+ if (strcasecmp(inp, "serial") && strcasecmp(inp, "vmchannel")) {
+ printf("Invalid mode: %s\n", inp);
+ if (yesno("Use anyway", 1) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "listeners/serial/@mode", inp);
+ return 0;
+}
+
+
+static int
+backend_configure(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done;
+
+ printf("\n");
+ printf("Backend modules are responsible for routing requests to\n"
+ "the appropriate hypervisor or management layer.\n\n");
+
+ /* Default backend plugin */
+ if (sc_get(config, "fence_virtd/@backend", val,
+ sizeof(val))) {
+ strncpy(val, "libvirt", sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Backend module", val, inp, sizeof(inp));
+ if (plugin_find_backend(inp) == NULL) {
+ printf("No backend module named %s found!\n", inp);
+ if (yesno("Use this value anyway", 0) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "fence_virtd/@backend", inp);
+
+ if (!strcmp(inp, "libvirt")) {
+ backend_config_libvirt(config);
+ } else if (!strcmp(inp, "cpg")) {
+ backend_config_cpg(config);
+ }
+
+ return 0;
+}
+
+
+static int
+listener_configure(config_object_t *config)
+{
+ char val[4096];
+ char inp[4096];
+ int done;
+
+ printf("\n");
+ printf("Listener modules are responsible for accepting requests\n"
+ "from fencing clients.\n\n");
+
+ /* Default backend plugin */
+ if (sc_get(config, "fence_virtd/@listener", val,
+ sizeof(val))) {
+ strncpy(val, "multicast", sizeof(val));
+ }
+
+ done = 0;
+ do {
+ text_input("Listener module", val, inp, sizeof(inp));
+ if (plugin_find_listener(inp) == NULL) {
+ printf("No listener module named %s found!\n", inp);
+ if (yesno("Use this value anyway", 0) == 1)
+ done = 1;
+ } else
+ done = 1;
+ } while (!done);
+
+ sc_set(config, "fence_virtd/@listener", inp);
+ if (!strcmp(inp, "multicast"))
+ listener_config_multicast(config);
+ else if (!strcmp(inp, "tcp"))
+ listener_config_tcp(config);
+ else if (!strcmp(inp, "serial"))
+ listener_config_serial(config);
+ else
+ printf("Unable to configure unknown listner module '%s'\n", inp);
+
+ return 0;
+}
+
+
+int
+check_file_permissions(const char *fname)
+{
+ struct stat st;
+ mode_t file_perms = 0600;
+ int ret;
+
+ ret = stat(fname, &st);
+ if (ret != 0) {
+ printf("stat failed on file '%s': %s\n",
+ fname, strerror(errno));
+ return 1;
+ }
+
+ if ((st.st_mode & 0777) != file_perms) {
+ printf("Insecure permissions on file "
+ "'%s': changing from 0%o to 0%o.\n", fname,
+ (unsigned int)(st.st_mode & 0777),
+ (unsigned int)file_perms);
+ if (chmod(fname, file_perms) != 0) {
+ printf("Unable to change permissions for file '%s'",
+ fname);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+do_configure(config_object_t *config, const char *config_file)
+{
+ FILE *fp = NULL;
+ char message[80];
+ char tmp_filename[4096];
+ int tmp_fd = -1;
+ mode_t old_umask;
+
+ if (sc_parse(config, config_file) != 0) {
+ printf("Parsing of %s failed.\n", config_file);
+ if (yesno("Start from scratch", 0) == 0) {
+ return 1;
+ }
+ }
+
+ plugin_path_configure(config);
+ listener_configure(config);
+ backend_configure(config);
+
+ printf("\nConfiguration complete.\n\n");
+
+ printf("=== Begin Configuration ===\n");
+ sc_dump(config, stdout);
+ printf("=== End Configuration ===\n");
+
+ snprintf(message, sizeof(message), "Replace %s with the above",
+ config_file);
+ if (yesno(message, 0) == 0) {
+ return 1;
+ }
+
+ snprintf(tmp_filename, sizeof(tmp_filename),
+ "%s.XXXXXX", config_file);
+
+ old_umask = umask(077);
+ tmp_fd = mkstemp(tmp_filename);
+ umask(old_umask);
+
+ if (tmp_fd < 0) {
+ perror("fopen");
+ printf("Failed to write configuration file!\n");
+ return 1;
+ }
+
+ fp = fdopen(tmp_fd, "w+");
+ if (fp == NULL)
+ goto out_fail;
+
+ sc_dump(config, fp);
+
+ if (rename(tmp_filename, config_file) < 0) {
+ perror("rename");
+ goto out_fail;
+ }
+
+ fclose(fp);
+ close(tmp_fd);
+
+ return 0;
+
+out_fail:
+ if (fp)
+ fclose(fp);
+ if (tmp_fd >= 0)
+ close(tmp_fd);
+ if (strlen(tmp_filename))
+ unlink(tmp_filename);
+ printf("Failed to write configuration file!\n");
+ return 1;
+}
diff --git a/agents/virt/server/cpg-virt.c b/agents/virt/server/cpg-virt.c
new file mode 100644
index 0000000..304519c
--- /dev/null
+++ b/agents/virt/server/cpg-virt.c
@@ -0,0 +1,643 @@
+/*
+ Copyright Red Hat, Inc. 2017
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Ryan McCabe <rmccabe@redhat.com>
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <time.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <corosync/cpg.h>
+
+#include "debug.h"
+#include "virt.h"
+#include "xvm.h"
+#include "cpg.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+
+#define NAME "cpg"
+#define CPG_VERSION "0.1"
+
+#define MAGIC 0x38e93fc2
+
+struct cpg_info {
+ int magic;
+ config_object_t *config;
+ int vp_count;
+ virConnectPtr *vp;
+};
+
+#define VALIDATE(arg) \
+do {\
+ if (!arg || ((struct cpg_info *) arg)->magic != MAGIC) { \
+ errno = EINVAL;\
+ return -1; \
+ } \
+} while(0)
+
+static struct cpg_info *cpg_virt_handle = NULL;
+static int use_uuid = 0;
+pthread_mutex_t local_vm_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static virt_list_t *local_vm_list = NULL;
+
+pthread_mutex_t remote_vm_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static virt_list_t *remote_vm_list = NULL;
+
+static void cpg_virt_init_libvirt(struct cpg_info *info);
+
+static int
+virt_list_update(struct cpg_info *info, virt_list_t **vl, int my_id)
+{
+ virt_list_t *list = NULL;
+
+ if (*vl)
+ vl_free(*vl);
+
+ list = vl_get(info->vp, info->vp_count, my_id);
+ if (!list && (errno == EPIPE || errno == EINVAL)) {
+ do {
+ cpg_virt_init_libvirt(info);
+ } while (info->vp_count == 0);
+ list = vl_get(info->vp, info->vp_count, my_id);
+ }
+
+ *vl = list;
+ if (!list)
+ return -1;
+
+ return 0;
+}
+
+
+static void
+store_domains(virt_list_t *vl)
+{
+ int i;
+
+ if (!vl)
+ return;
+
+ for (i = 0 ; i < vl->vm_count ; i++) {
+ int ret;
+
+ if (!strcmp(DOMAIN0NAME, vl->vm_states[i].v_name))
+ continue;
+
+ ret = cpg_send_vm_state(&vl->vm_states[i]);
+ if (ret < 0) {
+ printf("Error storing VM state for %s|%s\n",
+ vl->vm_states[i].v_name, vl->vm_states[i].v_uuid);
+ }
+ }
+}
+
+
+static void
+update_local_vms(struct cpg_info *info)
+{
+ uint32_t my_id = 0;
+
+ if (!info)
+ return;
+
+ cpg_get_ids(&my_id, NULL);
+ virt_list_update(info, &local_vm_list, my_id);
+ store_domains(local_vm_list);
+}
+
+
+static int
+do_off(struct cpg_info *info, const char *vm_name)
+{
+ dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name);
+ return vm_off(info->vp, info->vp_count, vm_name);
+
+}
+
+static int
+do_on(struct cpg_info *info, const char *vm_name)
+{
+ dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name);
+ return vm_on(info->vp, info->vp_count, vm_name);
+
+}
+
+static int
+do_reboot(struct cpg_info *info, const char *vm_name)
+{
+ dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name);
+ return vm_reboot(info->vp, info->vp_count, vm_name);
+}
+
+static void
+cpg_join_cb(const struct cpg_address *join, size_t joinlen) {
+ struct cpg_info *info = cpg_virt_handle;
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+}
+
+static void
+cpg_leave_cb(const struct cpg_address *left, size_t leftlen) {
+ struct cpg_info *info = cpg_virt_handle;
+ int i;
+
+ pthread_mutex_lock(&remote_vm_list_lock);
+ for (i = 0 ; i < leftlen ; i++) {
+ dbg_printf(2, "Removing VMs owned by nodeid %u\n", left[i].nodeid);
+ vl_remove_by_owner(&remote_vm_list, left[i].nodeid);
+ }
+ pthread_mutex_unlock(&remote_vm_list_lock);
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+}
+
+static void
+store_cb(void *data, size_t len, uint32_t nodeid, uint32_t seqno)
+{
+ uint32_t my_id;
+ virt_state_t *vs = (virt_state_t *) data;
+ struct cpg_info *info = cpg_virt_handle;
+
+ cpg_get_ids(&my_id, NULL);
+
+ if (nodeid == my_id)
+ return;
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ if (!local_vm_list)
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ pthread_mutex_lock(&remote_vm_list_lock);
+ vl_update(&remote_vm_list, vs);
+ pthread_mutex_unlock(&remote_vm_list_lock);
+}
+
+/*
+** This function must a send reply from at least one node, otherwise
+** the requesting fence_virtd will block forever in wait_cpt_reply.
+*/
+static void
+do_real_work(void *data, size_t len, uint32_t nodeid, uint32_t seqno)
+{
+ struct cpg_info *info = cpg_virt_handle;
+ struct cpg_fence_req *req = data;
+ struct cpg_fence_req reply;
+ int reply_code = -1;
+ virt_state_t *vs = NULL;
+ int cur_state;
+ uint32_t cur_owner = 0;
+ int local = 0;
+ uint32_t my_id, high_id;
+
+ dbg_printf(2, "Request %d for VM %s\n", req->request, req->vm_name);
+
+ if (cpg_get_ids(&my_id, &high_id) == -1) {
+ syslog(LOG_WARNING, "Unable to get CPG IDs");
+ printf("Should never happen: Can't get CPG node ids - can't proceed\n");
+ return;
+ }
+
+ memcpy(&reply, req, sizeof(reply));
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ if (strlen(req->vm_name)) {
+ if (use_uuid)
+ vs = vl_find_uuid(local_vm_list, req->vm_name);
+ else
+ vs = vl_find_name(local_vm_list, req->vm_name);
+
+ if (vs) {
+ local = 1;
+ cur_owner = vs->v_state.s_owner;
+ cur_state = vs->v_state.s_state;
+ dbg_printf(2, "Found VM %s locally state %d\n",
+ req->vm_name, cur_state);
+ }
+ }
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ if (vs == NULL) {
+ pthread_mutex_lock(&remote_vm_list_lock);
+ if (strlen(req->vm_name)) {
+ if (use_uuid)
+ vs = vl_find_uuid(remote_vm_list, req->vm_name);
+ else
+ vs = vl_find_name(remote_vm_list, req->vm_name);
+
+ if (vs) {
+ cur_owner = vs->v_state.s_owner;
+ cur_state = vs->v_state.s_state;
+ dbg_printf(2, "Found VM %s remotely on %u state %d\n",
+ req->vm_name, cur_owner, cur_state);
+ }
+ }
+ pthread_mutex_unlock(&remote_vm_list_lock);
+ }
+
+ if (!vs) {
+ /*
+ ** We know about all domains on all nodes in the CPG group.
+ ** If we didn't find it, and we're high ID, act on the request.
+ ** We can safely assume the VM is OFF because it wasn't found
+ ** on any current members of the CPG group.
+ */
+ if (my_id == high_id) {
+ if (req->request == FENCE_STATUS)
+ reply_code = RESP_OFF;
+ else if (req->request == FENCE_OFF || req->request == FENCE_REBOOT)
+ reply_code = RESP_SUCCESS;
+ else
+ reply_code = 1;
+
+ dbg_printf(2, "Acting on request %d for unknown domain %s -> %d\n",
+ req->request, req->vm_name, reply_code);
+ goto out;
+ }
+
+ dbg_printf(2, "Not acting on request %d for unknown domain %s\n",
+ req->request, req->vm_name);
+ return;
+ }
+
+ if (local) {
+ if (req->request == FENCE_STATUS) {
+ /* We already have the status */
+ if (cur_state == VIR_DOMAIN_SHUTOFF)
+ reply_code = RESP_OFF;
+ else
+ reply_code = RESP_SUCCESS;
+ } else if (req->request == FENCE_OFF) {
+ reply_code = do_off(info, req->vm_name);
+ } else if (req->request == FENCE_ON) {
+ reply_code = do_on(info, req->vm_name);
+ } else if (req->request == FENCE_REBOOT) {
+ reply_code = do_reboot(info, req->vm_name);
+ } else {
+ dbg_printf(2, "Not explicitly handling request type %d for %s\n",
+ req->request, req->vm_name);
+ reply_code = 0;
+ }
+ goto out;
+ }
+
+ /*
+ ** This is a request for a non-local domain that exists on a
+ ** current CPG group member, so that member will see the request
+ ** and act on it. We don't need to do anything.
+ */
+ dbg_printf(2, "Nothing to do for non-local domain %s seq %d owner %u\n",
+ req->vm_name, seqno, cur_owner);
+ return;
+
+out:
+ dbg_printf(2, "[%s] sending reply code seq %d -> %d\n",
+ req->vm_name, seqno, reply_code);
+
+ reply.response = reply_code;
+ if (cpg_send_reply(&reply, sizeof(reply), nodeid, seqno) < 0) {
+ dbg_printf(2, "cpg_send_reply failed for %s [%d %d]: %s\n",
+ req->vm_name, nodeid, seqno, strerror(errno));
+ }
+}
+
+
+static int
+do_request(const char *vm_name, int request, uint32_t seqno)
+{
+ struct cpg_fence_req freq, *frp;
+ size_t retlen;
+ uint32_t seq;
+ int ret;
+
+ memset(&freq, 0, sizeof(freq));
+ if (!vm_name) {
+ dbg_printf(1, "No VM name\n");
+ return 1;
+ }
+
+ if (strlen(vm_name) >= sizeof(freq.vm_name)) {
+ dbg_printf(1, "VM name %s too long\n", vm_name);
+ return 1;
+ }
+
+ strcpy(freq.vm_name, vm_name);
+
+ freq.request = request;
+ freq.seqno = seqno;
+
+ if (cpg_send_req(&freq, sizeof(freq), &seq) != 0) {
+ dbg_printf(1, "Failed to send request %d for VM %s\n",
+ freq.request, vm_name);
+ return 1;
+ }
+
+ dbg_printf(2, "Sent request %d for VM %s got seqno %d\n",
+ request, vm_name, seq);
+
+ if (cpg_wait_reply((void *) &frp, &retlen, seq) != 0) {
+ dbg_printf(1, "Failed to receive reply seq %d for %s\n", seq, vm_name);
+ return 1;
+ }
+
+ dbg_printf(2, "Received reply [%d] seq %d for %s\n",
+ frp->response, seq, vm_name);
+
+ ret = frp->response;
+ free(frp);
+
+ return ret;
+}
+
+
+static int
+cpg_virt_null(const char *vm_name, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] Null operation on %s\n", vm_name);
+
+ return 1;
+}
+
+
+static int
+cpg_virt_off(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] OFF operation on %s seq %d\n", vm_name, seqno);
+
+ return do_request(vm_name, FENCE_OFF, seqno);
+}
+
+
+static int
+cpg_virt_on(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] ON operation on %s seq %d\n", vm_name, seqno);
+
+ return do_request(vm_name, FENCE_ON, seqno);
+}
+
+
+static int
+cpg_virt_devstatus(void *priv)
+{
+ printf("[cpg-virt] Device status\n");
+ VALIDATE(priv);
+
+ return 0;
+}
+
+
+static int
+cpg_virt_status(const char *vm_name, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] STATUS operation on %s\n", vm_name);
+
+ return do_request(vm_name, FENCE_STATUS, 0);
+}
+
+
+static int
+cpg_virt_reboot(const char *vm_name, const char *src,
+ uint32_t seqno, void *priv)
+{
+ VALIDATE(priv);
+ printf("[cpg-virt] REBOOT operation on %s seq %d\n", vm_name, seqno);
+
+ return do_request(vm_name, FENCE_REBOOT, 0);
+}
+
+
+static int
+cpg_virt_hostlist(hostlist_callback callback, void *arg, void *priv)
+{
+ struct cpg_info *info = (struct cpg_info *) priv;
+ int i;
+
+ VALIDATE(priv);
+ printf("[cpg-virt] HOSTLIST operation\n");
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ for (i = 0 ; i < local_vm_list->vm_count ; i++) {
+ callback(local_vm_list->vm_states[i].v_name,
+ local_vm_list->vm_states[i].v_uuid,
+ local_vm_list->vm_states[i].v_state.s_state, arg);
+ }
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ return 1;
+}
+
+static void
+cpg_virt_init_libvirt(struct cpg_info *info) {
+ config_object_t *config = info->config;
+ int i = 0;
+
+ if (info->vp) {
+ dbg_printf(2, "Lost libvirtd connection. Reinitializing.\n");
+ for (i = 0 ; i < info->vp_count ; i++)
+ virConnectClose(info->vp[i]);
+ free(info->vp);
+ info->vp = NULL;
+ }
+ info->vp_count = 0;
+
+ do {
+ virConnectPtr vp;
+ virConnectPtr *vpl = NULL;
+ char conf_attr[256];
+ char value[1024];
+ char *uri;
+
+ if (i != 0) {
+ snprintf(conf_attr, sizeof(conf_attr),
+ "backends/cpg/@uri%d", i);
+ } else
+ snprintf(conf_attr, sizeof(conf_attr), "backends/cpg/@uri");
+ ++i;
+
+ if (sc_get(config, conf_attr, value, sizeof(value)) != 0)
+ break;
+
+ uri = value;
+ vp = virConnectOpen(uri);
+ if (!vp) {
+ dbg_printf(1, "[cpg-virt:INIT] Failed to connect to URI: %s\n", uri);
+ continue;
+ }
+
+ vpl = realloc(info->vp, sizeof(*info->vp) * (info->vp_count + 1));
+ if (!vpl) {
+ dbg_printf(1, "[cpg-virt:INIT] Out of memory allocating URI: %s\n",
+ uri);
+ virConnectClose(vp);
+ continue;
+ }
+
+ info->vp = vpl;
+ info->vp[info->vp_count++] = vp;
+
+ if (i > 1)
+ dbg_printf(1, "[cpg-virt:INIT] Added URI%d %s\n", i - 1, uri);
+ else
+ dbg_printf(1, "[cpg_virt:INIT] Added URI %s\n", uri);
+ } while (1);
+}
+
+static int
+cpg_virt_init(backend_context_t *c, config_object_t *config)
+{
+ char value[1024];
+ struct cpg_info *info = NULL;
+ int ret;
+
+ ret = cpg_start(PACKAGE_NAME,
+ do_real_work, store_cb, cpg_join_cb, cpg_leave_cb);
+ if (ret < 0)
+ return -1;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+ info->magic = MAGIC;
+ info->config = config;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0)
+ dset(atoi(value));
+
+ cpg_virt_init_libvirt(info);
+
+ /* Naming scheme is no longer a top-level config option.
+ * However, we retain it here for configuration compatibility with
+ * versions 0.1.3 and previous.
+ */
+ if (sc_get(config, "fence_virtd/@name_mode",
+ value, sizeof(value)-1) == 0) {
+
+ dbg_printf(1, "Got %s for name_mode\n", value);
+ if (!strcasecmp(value, "uuid")) {
+ use_uuid = 1;
+ } else if (!strcasecmp(value, "name")) {
+ use_uuid = 0;
+ } else {
+ dbg_printf(1, "Unsupported name_mode: %s\n", value);
+ }
+ }
+
+ if (sc_get(config, "backends/cpg/@name_mode",
+ value, sizeof(value)-1) == 0)
+ {
+ dbg_printf(1, "Got %s for name_mode\n", value);
+ if (!strcasecmp(value, "uuid")) {
+ use_uuid = 1;
+ } else if (!strcasecmp(value, "name")) {
+ use_uuid = 0;
+ } else {
+ dbg_printf(1, "Unsupported name_mode: %s\n", value);
+ }
+ }
+
+ if (info->vp_count < 1) {
+ dbg_printf(1, "[cpg_virt:INIT] Could not connect to any hypervisors\n");
+ cpg_stop();
+ free(info);
+ return -1;
+ }
+
+ pthread_mutex_lock(&local_vm_list_lock);
+ update_local_vms(info);
+ pthread_mutex_unlock(&local_vm_list_lock);
+
+ *c = (void *) info;
+ cpg_virt_handle = info;
+ return 0;
+}
+
+
+static int
+cpg_virt_shutdown(backend_context_t c)
+{
+ struct cpg_info *info = (struct cpg_info *)c;
+ int i = 0;
+ int ret = 0;
+
+ VALIDATE(info);
+ info->magic = 0;
+
+ cpg_stop();
+
+ for (i = 0 ; i < info->vp_count ; i++) {
+ if (virConnectClose(info->vp[i]) < 0)
+ ret = -errno;
+ }
+
+ free(info->vp);
+ free(info);
+
+ return ret;
+}
+
+
+static fence_callbacks_t cpg_callbacks = {
+ .null = cpg_virt_null,
+ .off = cpg_virt_off,
+ .on = cpg_virt_on,
+ .reboot = cpg_virt_reboot,
+ .status = cpg_virt_status,
+ .devstatus = cpg_virt_devstatus,
+ .hostlist = cpg_virt_hostlist
+};
+
+static backend_plugin_t cpg_virt_plugin = {
+ .name = NAME,
+ .version = CPG_VERSION,
+ .callbacks = &cpg_callbacks,
+ .init = cpg_virt_init,
+ .cleanup = cpg_virt_shutdown,
+};
+
+double
+BACKEND_VER_SYM(void)
+{
+ return PLUGIN_VERSION_BACKEND;
+}
+
+const backend_plugin_t *
+BACKEND_INFO_SYM(void)
+{
+ return &cpg_virt_plugin;
+}
diff --git a/agents/virt/server/cpg.c b/agents/virt/server/cpg.c
new file mode 100644
index 0000000..8f84cd8
--- /dev/null
+++ b/agents/virt/server/cpg.c
@@ -0,0 +1,411 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <list.h>
+#include <pthread.h>
+
+#include <corosync/cpg.h>
+
+#include "debug.h"
+#include "virt.h"
+#include "cpg.h"
+
+#define NODE_ID_NONE ((uint32_t) -1)
+
+struct msg_queue_node {
+ list_head();
+ uint32_t seqno;
+#define STATE_CLEAR 0
+#define STATE_MESSAGE 1
+ uint32_t state;
+ void *msg;
+ size_t msglen;
+};
+
+struct wire_msg {
+#define TYPE_REQUEST 0
+#define TYPE_REPLY 1
+#define TYPE_STORE_VM 2
+ uint32_t type;
+ uint32_t seqno;
+ uint32_t target;
+ uint32_t pad;
+ char data[0];
+};
+
+static uint32_t seqnum = 0;
+static struct msg_queue_node *pending = NULL;
+static cpg_handle_t cpg_handle;
+static struct cpg_name gname;
+
+static pthread_mutex_t cpg_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cpg_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t cpg_thread = 0;
+
+static pthread_mutex_t cpg_ids_mutex = PTHREAD_MUTEX_INITIALIZER;
+static uint32_t my_node_id = NODE_ID_NONE;
+static uint32_t high_id_from_callback = NODE_ID_NONE;
+
+static request_callback_fn req_callback_fn;
+static request_callback_fn store_callback_fn;
+static confchange_callback_fn conf_leave_fn;
+static confchange_callback_fn conf_join_fn;
+
+
+int
+cpg_get_ids(uint32_t *my_id, uint32_t *high_id)
+{
+ if (!my_id && !high_id)
+ return -1;
+
+ pthread_mutex_lock(&cpg_ids_mutex);
+ if (my_id)
+ *my_id = my_node_id;
+
+ if (high_id)
+ *high_id = high_id_from_callback;
+ pthread_mutex_unlock(&cpg_ids_mutex);
+
+ return 0;
+}
+
+static void
+cpg_deliver_func(cpg_handle_t h,
+ const struct cpg_name *group_name,
+ uint32_t nodeid,
+ uint32_t pid,
+ void *msg,
+ size_t msglen)
+{
+ struct msg_queue_node *n;
+ struct wire_msg *m = msg;
+ int x, found;
+
+ pthread_mutex_lock(&cpg_mutex);
+ if (m->type == TYPE_REPLY) {
+ /* Reply to a request we sent */
+ found = 0;
+
+ list_for(&pending, n, x) {
+ if (m->seqno != n->seqno)
+ continue;
+ if (m->target != my_node_id)
+ continue;
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ goto out_unlock;
+
+ /* Copy our message in to a buffer */
+ n->msglen = msglen - sizeof(*m);
+ if (!n->msglen) {
+ /* XXX do what? */
+ }
+ n->msg = malloc(n->msglen);
+ if (!n->msg) {
+ goto out_unlock;
+ }
+ n->state = STATE_MESSAGE;
+ memcpy(n->msg, (char *)msg + sizeof(*m), n->msglen);
+
+ list_remove(&pending, n);
+ list_insert(&pending, n);
+
+ dbg_printf(2, "Seqnum %d replied; removing from list\n", n->seqno);
+
+ pthread_cond_broadcast(&cpg_cond);
+ goto out_unlock;
+ }
+ pthread_mutex_unlock(&cpg_mutex);
+
+ if (m->type == TYPE_REQUEST) {
+ req_callback_fn(&m->data, msglen - sizeof(*m),
+ nodeid, m->seqno);
+ }
+ if (m->type == TYPE_STORE_VM) {
+ store_callback_fn(&m->data, msglen - sizeof(*m),
+ nodeid, m->seqno);
+ }
+
+ return;
+
+out_unlock:
+ pthread_mutex_unlock(&cpg_mutex);
+}
+
+
+static void
+cpg_config_change(cpg_handle_t h,
+ const struct cpg_name *group_name,
+ const struct cpg_address *members, size_t memberlen,
+ const struct cpg_address *left, size_t leftlen,
+ const struct cpg_address *join, size_t joinlen)
+{
+ int x;
+ int high;
+
+ pthread_mutex_lock(&cpg_ids_mutex);
+ high = my_node_id;
+
+ for (x = 0; x < memberlen; x++) {
+ if (members[x].nodeid > high)
+ high = members[x].nodeid;
+ }
+
+ high_id_from_callback = high;
+ pthread_mutex_unlock(&cpg_ids_mutex);
+
+ if (joinlen > 0)
+ conf_join_fn(join, joinlen);
+
+ if (leftlen > 0)
+ conf_leave_fn(left, leftlen);
+}
+
+
+static cpg_callbacks_t my_callbacks = {
+ .cpg_deliver_fn = cpg_deliver_func,
+ .cpg_confchg_fn = cpg_config_change
+};
+
+
+int
+cpg_send_req(void *data, size_t len, uint32_t *seqno)
+{
+ struct iovec iov;
+ struct msg_queue_node *n;
+ struct wire_msg *m;
+ size_t msgsz = sizeof(*m) + len;
+ int ret;
+
+ n = malloc(sizeof(*n));
+ if (!n)
+ return -1;
+
+ m = malloc(msgsz);
+ if (!m) {
+ free(n);
+ return -1;
+ }
+
+ /* only incremented on send */
+ n->state = STATE_CLEAR;
+ n->msg = NULL;
+ n->msglen = 0;
+
+ pthread_mutex_lock(&cpg_mutex);
+ list_insert(&pending, n);
+ n->seqno = ++seqnum;
+ m->seqno = seqnum;
+ *seqno = seqnum;
+ pthread_mutex_unlock(&cpg_mutex);
+
+ m->type = TYPE_REQUEST; /* XXX swab? */
+ m->target = NODE_ID_NONE;
+ memcpy(&m->data, data, len);
+
+ iov.iov_base = m;
+ iov.iov_len = msgsz;
+ ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1);
+
+ free(m);
+ if (ret == CS_OK)
+ return 0;
+ return -1;
+}
+
+
+int
+cpg_send_vm_state(virt_state_t *vs)
+{
+ struct iovec iov;
+ struct msg_queue_node *n;
+ struct wire_msg *m;
+ size_t msgsz = sizeof(*m) + sizeof(*vs);
+ int ret;
+
+ n = calloc(1, (sizeof(*n)));
+ if (!n)
+ return -1;
+
+ m = calloc(1, msgsz);
+ if (!m) {
+ free(n);
+ return -1;
+ }
+
+ n->state = STATE_MESSAGE;
+ n->msg = NULL;
+ n->msglen = 0;
+
+ pthread_mutex_lock(&cpg_mutex);
+ list_insert(&pending, n);
+ pthread_mutex_unlock(&cpg_mutex);
+
+ m->type = TYPE_STORE_VM;
+ m->target = NODE_ID_NONE;
+
+ memcpy(&m->data, vs, sizeof(*vs));
+
+ iov.iov_base = m;
+ iov.iov_len = msgsz;
+ ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1);
+
+ free(m);
+ if (ret == CS_OK)
+ return 0;
+
+ return -1;
+}
+
+
+int
+cpg_send_reply(void *data, size_t len, uint32_t nodeid, uint32_t seqno)
+{
+ struct iovec iov;
+ struct wire_msg *m;
+ size_t msgsz = sizeof(*m) + len;
+ int ret;
+
+ m = malloc(msgsz);
+ if (!m)
+ return -1;
+
+ /* only incremented on send */
+ m->seqno = seqno;
+ m->type = TYPE_REPLY; /* XXX swab? */
+ m->target = nodeid;
+ memcpy(&m->data, data, len);
+
+ iov.iov_base = m;
+ iov.iov_len = msgsz;
+ ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1);
+
+ free(m);
+ if (ret == CS_OK)
+ return 0;
+
+ return -1;
+}
+
+
+int
+cpg_wait_reply(void **data, size_t *len, uint32_t seqno)
+{
+ struct msg_queue_node *n;
+ int x, found = 0;
+
+ while (!found) {
+ found = 0;
+ pthread_mutex_lock(&cpg_mutex);
+ pthread_cond_wait(&cpg_cond, &cpg_mutex);
+
+ list_for(&pending, n, x) {
+ if (n->seqno != seqno)
+ continue;
+ if (n->state != STATE_MESSAGE)
+ continue;
+ found = 1;
+ goto out;
+ }
+ pthread_mutex_unlock(&cpg_mutex);
+ }
+
+out:
+ list_remove(&pending, n);
+ pthread_mutex_unlock(&cpg_mutex);
+
+ *data = n->msg;
+ *len = n->msglen;
+ free(n);
+
+ return 0;
+}
+
+
+static void *
+cpg_dispatch_thread(void *arg)
+{
+ cpg_dispatch(cpg_handle, CS_DISPATCH_BLOCKING);
+
+ return NULL;
+}
+
+
+int
+cpg_start( const char *name,
+ request_callback_fn req_cb_fn,
+ request_callback_fn store_cb_fn,
+ confchange_callback_fn join_fn,
+ confchange_callback_fn leave_fn)
+{
+ cpg_handle_t h;
+ int ret;
+
+ errno = EINVAL;
+
+ if (!name)
+ return -1;
+
+ ret = snprintf(gname.value, sizeof(gname.value), "%s", name);
+ if (ret <= 0)
+ return -1;
+
+ if (ret >= sizeof(gname.value)) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ gname.length = ret;
+
+ memset(&h, 0, sizeof(h));
+ if (cpg_initialize(&h, &my_callbacks) != CS_OK) {
+ perror("cpg_initialize");
+ return -1;
+ }
+
+ if (cpg_join(h, &gname) != CS_OK) {
+ perror("cpg_join");
+ return -1;
+ }
+
+ cpg_local_get(h, &my_node_id);
+ dbg_printf(2, "My CPG nodeid is %d\n", my_node_id);
+
+ pthread_mutex_lock(&cpg_mutex);
+ pthread_create(&cpg_thread, NULL, cpg_dispatch_thread, NULL);
+
+ memcpy(&cpg_handle, &h, sizeof(h));
+
+ req_callback_fn = req_cb_fn;
+ store_callback_fn = store_cb_fn;
+ conf_join_fn = join_fn;
+ conf_leave_fn = leave_fn;
+
+ pthread_mutex_unlock(&cpg_mutex);
+
+ return 0;
+}
+
+
+int
+cpg_stop(void)
+{
+ pthread_cancel(cpg_thread);
+ pthread_join(cpg_thread, NULL);
+ cpg_leave(cpg_handle, &gname);
+ cpg_finalize(cpg_handle);
+
+ return 0;
+}
diff --git a/agents/virt/server/cpg.h b/agents/virt/server/cpg.h
new file mode 100644
index 0000000..6873955
--- /dev/null
+++ b/agents/virt/server/cpg.h
@@ -0,0 +1,29 @@
+#ifndef __FENCE_VIRTD_CPG_H
+#define __FENCE_VIRTD_CPG_H
+
+struct cpg_fence_req {
+ char vm_name[128];
+ int request;
+ uint32_t seqno;
+ uint32_t response;
+};
+
+typedef void (*request_callback_fn)(void *data, size_t len, uint32_t nodeid,
+ uint32_t seqno);
+typedef void (*confchange_callback_fn)(const struct cpg_address *m, size_t len);
+
+int cpg_start( const char *name,
+ request_callback_fn func,
+ request_callback_fn store_func,
+ confchange_callback_fn join,
+ confchange_callback_fn leave);
+
+int cpg_get_ids(uint32_t *me, uint32_t *high);
+int cpg_stop(void);
+int cpg_send_req(void *data, size_t len, uint32_t *seqno);
+int cpg_wait_reply(void **data, size_t *len, uint32_t seqno);
+int cpg_send_reply(void *data, size_t len, uint32_t nodeid, uint32_t seqno);
+int cpg_send_vm_state(virt_state_t *vs);
+
+
+#endif
diff --git a/agents/virt/server/daemon_init.c b/agents/virt/server/daemon_init.c
new file mode 100644
index 0000000..29b33ad
--- /dev/null
+++ b/agents/virt/server/daemon_init.c
@@ -0,0 +1,215 @@
+/** @file
+ * daemon_init function, does sanity checks and calls daemon().
+ *
+ * Author: Jeff Moyer <jmoyer@redhat.com>
+ */
+/*
+ * TODO: Clean this up so that only one function constructs the
+ * pidfile /var/run/loggerd.PID, and perhaps only one function
+ * forms the /proc/PID/ path.
+ *
+ * Also need to add file locking for the pid file.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <libgen.h>
+#include <signal.h>
+#include <syslog.h>
+
+
+/*
+ * This should ultimately go in a header file.
+ */
+void daemon_init(const char *prog, const char *pid_file, int nofork);
+void daemon_cleanup(void);
+int check_process_running(const char *cmd, const char *pid_file, pid_t * pid);
+
+/*
+ * Local prototypes.
+ */
+static void update_pidfile(const char *filename);
+static int setup_sigmask(void);
+static char pid_filename[PATH_MAX];
+
+static int
+check_pid_valid(pid_t pid, const char *prog)
+{
+ FILE *fp;
+ DIR *dir;
+ char filename[PATH_MAX];
+ char dirpath[PATH_MAX];
+ char proc_cmdline[64]; /* yank this from kernel somewhere */
+ char *s = NULL;
+
+ memset(filename, 0, PATH_MAX);
+ memset(dirpath, 0, PATH_MAX);
+
+ snprintf(dirpath, sizeof (dirpath), "/proc/%d", pid);
+ if ((dir = opendir(dirpath)) == NULL) {
+ return 0; /* Pid has gone away. */
+ }
+ closedir(dir);
+
+ /*
+ * proc-pid directory exists. Now check to see if this
+ * PID corresponds to the daemon we want to start.
+ */
+ snprintf(filename, sizeof (filename), "/proc/%d/cmdline", pid);
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ perror("check_pid_valid");
+ return 0; /* Who cares.... Let's boogy on. */
+ }
+
+ if (!fgets(proc_cmdline, sizeof (proc_cmdline) - 1, fp)) {
+ /*
+ * Okay, we've seen processes keep a reference to a
+ * /proc/PID/stat file and not let go. Then when
+ * you try to read /proc/PID/cmline, you get either
+ * \000 or -1. In either case, we can safely assume
+ * the process has gone away.
+ */
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ s = &(proc_cmdline[strlen(proc_cmdline)]);
+ if (*s == '\n')
+ *s = 0;
+
+ /*
+ * Check to see if this is the same executable.
+ */
+ if (strstr(proc_cmdline, prog) == NULL) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+int
+check_process_running(const char *cmd, const char *filename, pid_t * pid)
+{
+ pid_t oldpid;
+ FILE *fp = NULL;
+ int ret;
+
+ *pid = -1;
+
+ /*
+ * Read the pid from the file.
+ */
+ fp = fopen(filename, "r");
+ if (fp == NULL) { /* error */
+ return 0;
+ }
+
+ ret = fscanf(fp, "%d\n", &oldpid);
+ fclose(fp);
+
+ if ((ret == EOF) || (ret != 1))
+ return 0;
+
+ if (check_pid_valid(oldpid, cmd)) {
+ *pid = oldpid;
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+update_pidfile(const char *filename)
+{
+ FILE *fp = NULL;
+
+ strncpy(pid_filename, filename, PATH_MAX - 1);
+
+ fp = fopen(pid_filename, "w");
+ if (fp == NULL) {
+ syslog(LOG_ERR, "daemon_init: Unable to create pidfile %s: %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+
+ fprintf(fp, "%d", getpid());
+ fclose(fp);
+}
+
+
+static int
+setup_sigmask(void)
+{
+ sigset_t set;
+
+ sigfillset(&set);
+
+ /*
+ * Dont't block signals which would cause us to dump core.
+ */
+ sigdelset(&set, SIGQUIT);
+ sigdelset(&set, SIGILL);
+ sigdelset(&set, SIGTRAP);
+ sigdelset(&set, SIGABRT);
+ sigdelset(&set, SIGFPE);
+ sigdelset(&set, SIGSEGV);
+ sigdelset(&set, SIGBUS);
+
+ /*
+ * Don't block SIGTERM or SIGCHLD
+ */
+ sigdelset(&set, SIGTERM);
+ sigdelset(&set, SIGINT);
+ sigdelset(&set, SIGQUIT);
+ sigdelset(&set, SIGCHLD);
+
+ return (sigprocmask(SIG_BLOCK, &set, NULL));
+}
+
+
+void
+daemon_init(const char *prog, const char *pid_file, int nofork)
+{
+ pid_t pid;
+
+ if (check_process_running(prog, pid_file, &pid) && (pid != getpid())) {
+ syslog(LOG_ERR,
+ "daemon_init: Process \"%s\" already running.\n",
+ prog);
+ exit(1);
+ }
+
+ if (setup_sigmask() < 0) {
+ syslog(LOG_ERR, "daemon_init: Unable to set signal mask.\n");
+ exit(1);
+ }
+
+ if (!nofork && daemon(0, 0)) {
+ syslog(LOG_ERR, "daemon_init: Unable to daemonize.\n");
+ exit(1);
+ }
+
+ update_pidfile(pid_file);
+}
+
+
+void
+daemon_cleanup(void)
+{
+ if (strlen(pid_filename))
+ unlink(pid_filename);
+}
diff --git a/agents/virt/server/history.c b/agents/virt/server/history.c
new file mode 100644
index 0000000..bd3a68c
--- /dev/null
+++ b/agents/virt/server/history.c
@@ -0,0 +1,124 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <list.h>
+#include <time.h>
+
+#include "history.h"
+
+history_info_t *
+history_init(history_compare_fn func, time_t expiration, size_t element_size)
+{
+ history_info_t *hist;
+
+ errno = EINVAL;
+ if (!func || !expiration || !element_size)
+ return NULL;
+
+ hist = malloc(sizeof(*hist));
+ if (!hist)
+ return NULL;
+ memset(hist, 0, sizeof(*hist));
+
+ hist->timeout = expiration;
+ hist->element_size = element_size;
+ hist->compare_func = func;
+
+ return hist;
+}
+
+
+/*
+ * Purge our history when the entries time out.
+ *
+ * Returns 1 if a matching history node was found, or 0
+ * if not.
+ */
+int
+history_check(history_info_t *hinfo, void *stuff)
+{
+ history_node *entry = NULL;
+ time_t now;
+ int x;
+
+ if (!hinfo)
+ return 0; /* XXX */
+
+ if (!hinfo->hist)
+ return 0;
+
+ now = time(NULL);
+
+loop_again:
+ list_for((&hinfo->hist), entry, x) {
+ if (entry->when < (now - hinfo->timeout)) {
+ list_remove((&hinfo->hist), entry);
+ free(entry->data);
+ free(entry);
+ goto loop_again;
+ }
+
+ if (hinfo->compare_func(entry->data, stuff)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+int
+history_record(history_info_t *hinfo, void *data)
+{
+ history_node *entry = NULL;
+
+ errno = EINVAL;
+ if (!data || !hinfo)
+ return -1;
+
+ if (history_check(hinfo, data) == 1) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ entry = malloc(sizeof(*entry));
+ if (!entry) {
+ return -1;
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ entry->data = malloc(hinfo->element_size);
+ if (!entry->data) {
+ free(entry);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(entry->data, data, hinfo->element_size);
+ entry->when = time(NULL);
+ list_insert((&hinfo->hist), entry);
+ return 0;
+}
+
+
+int
+history_wipe(history_info_t *hinfo)
+{
+ history_node *entry = NULL;
+
+ if (!hinfo)
+ return -1;
+
+ while (hinfo->hist) {
+ entry = hinfo->hist;
+ list_remove((&hinfo->hist), entry);
+ free(entry->data);
+ free(entry);
+ }
+
+ /* User must free(hinfo); */
+ return 0;
+}
diff --git a/agents/virt/server/libvirt.c b/agents/virt/server/libvirt.c
new file mode 100644
index 0000000..8f01045
--- /dev/null
+++ b/agents/virt/server/libvirt.c
@@ -0,0 +1,359 @@
+/*
+ Copyright Red Hat, Inc. 2006-2017
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <libvirt/virterror.h>
+#include <nss.h>
+#include <libgen.h>
+#include <syslog.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "virt.h"
+#include "debug.h"
+#include "uuid-test.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+
+#define NAME "libvirt"
+#define LIBVIRT_VERSION "0.3"
+
+#define MAGIC 0x1e19317a
+
+struct libvirt_info {
+ int magic;
+ config_object_t *config;
+ int vp_count;
+ virConnectPtr *vp;
+};
+
+#define VALIDATE(arg) \
+do {\
+ if (!arg || ((struct libvirt_info *)arg)->magic != MAGIC) { \
+ errno = EINVAL;\
+ return -1; \
+ } \
+} while(0)
+
+
+static void
+libvirt_init_libvirt_conf(struct libvirt_info *info) {
+ config_object_t *config = info->config;
+ int i = 0;
+
+ if (info->vp) {
+ dbg_printf(2, "Lost libvirtd connection. Reinitializing.\n");
+ for (i = 0 ; i < info->vp_count ; i++)
+ virConnectClose(info->vp[i]);
+ free(info->vp);
+ info->vp = NULL;
+ }
+ info->vp_count = 0;
+
+ do {
+ virConnectPtr vp;
+ virConnectPtr *vpl = NULL;
+ char conf_attr[256];
+ char value[1024];
+ char *uri;
+
+ if (i != 0) {
+ snprintf(conf_attr, sizeof(conf_attr),
+ "backends/libvirt/@uri%d", i);
+ } else
+ snprintf(conf_attr, sizeof(conf_attr), "backends/libvirt/@uri");
+ ++i;
+
+ if (sc_get(config, conf_attr, value, sizeof(value)) != 0)
+ break;
+
+ uri = value;
+ vp = virConnectOpen(uri);
+ if (!vp) {
+ dbg_printf(1, "[libvirt:INIT] Failed to connect to URI: %s\n", uri);
+ continue;
+ }
+
+ vpl = realloc(info->vp, sizeof(*info->vp) * (info->vp_count + 1));
+ if (!vpl) {
+ dbg_printf(1, "[libvirt:INIT] Out of memory allocating URI: %s\n",
+ uri);
+ virConnectClose(vp);
+ continue;
+ }
+
+ info->vp = vpl;
+ info->vp[info->vp_count++] = vp;
+
+ if (i > 1)
+ dbg_printf(1, "[libvirt:INIT] Added URI%d %s\n", i - 1, uri);
+ else
+ dbg_printf(1, "[libvirt:INIT] Added URI %s\n", uri);
+ } while (1);
+}
+
+
+static int
+libvirt_bad_connections(struct libvirt_info *info) {
+ int bad = 0;
+ int i;
+
+ for (i = 0 ; i < info->vp_count ; i++) {
+ /*
+ ** Send a dummy command to trigger an error if libvirtd
+ ** died or restarted
+ */
+ virConnectNumOfDomains(info->vp[i]);
+ if (!virConnectIsAlive(info->vp[i])) {
+ dbg_printf(1, "libvirt connection %d is dead\n", i);
+ bad++;
+ }
+ }
+
+ if (info->vp_count < 1 || bad)
+ libvirt_init_libvirt_conf(info);
+
+ return bad || info->vp_count < 1;
+}
+
+static void
+libvirt_validate_connections(struct libvirt_info *info) {
+ while (1) {
+ if (libvirt_bad_connections(info))
+ sleep(1);
+ else
+ break;
+ }
+}
+
+static int
+libvirt_null(const char *vm_name, void *priv)
+{
+ dbg_printf(5, "ENTER %s %s\n", __FUNCTION__, vm_name);
+ printf("NULL operation: returning failure\n");
+ return 1;
+}
+
+
+static int
+libvirt_off(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_off(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_on(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_on(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_devstatus(void *priv)
+{
+ dbg_printf(5, "%s ---\n", __FUNCTION__);
+
+ if (priv)
+ return 0;
+ return 1;
+}
+
+
+static int
+libvirt_status(const char *vm_name, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s\n", __FUNCTION__, vm_name);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_status(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_reboot(const char *vm_name, const char *src, uint32_t seqno, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+
+ dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+ return vm_reboot(info->vp, info->vp_count, vm_name);
+}
+
+
+static int
+libvirt_hostlist(hostlist_callback callback, void *arg, void *priv)
+{
+ struct libvirt_info *info = (struct libvirt_info *)priv;
+ virt_list_t *vl;
+ int x;
+
+ dbg_printf(5, "ENTER %s\n", __FUNCTION__);
+ VALIDATE(info);
+
+ libvirt_validate_connections(info);
+
+ vl = vl_get(info->vp, info->vp_count, 1);
+ if (!vl)
+ return 0;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ callback(vl->vm_states[x].v_name,
+ vl->vm_states[x].v_uuid,
+ vl->vm_states[x].v_state.s_state, arg);
+
+ dbg_printf(10, "[libvirt:HOSTLIST] Sent %s %s %d\n",
+ vl->vm_states[x].v_name,
+ vl->vm_states[x].v_uuid,
+ vl->vm_states[x].v_state.s_state);
+ }
+
+ vl_free(vl);
+ return 0;
+}
+
+
+static int
+libvirt_init(backend_context_t *c, config_object_t *config)
+{
+ char value[256];
+ struct libvirt_info *info = NULL;
+
+ dbg_printf(5, "ENTER [%s:%d %s]\n", __FILE__, __LINE__, __FUNCTION__);
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+ info->magic = MAGIC;
+ info->config = config;
+
+ libvirt_init_libvirt_conf(info);
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0)
+ dset(atoi(value));
+
+ if (info->vp_count < 1) {
+ dbg_printf(1, "[libvirt:INIT] Could not connect to any hypervisors\n");
+ if (info->vp)
+ free(info->vp);
+ free(info);
+ return -1;
+ }
+
+ *c = (void *) info;
+ return 0;
+}
+
+
+static int
+libvirt_shutdown(backend_context_t c)
+{
+ struct libvirt_info *info = (struct libvirt_info *)c;
+ int i;
+ int ret = 0;
+
+ VALIDATE(info);
+
+ for (i = 0 ; i < info->vp_count ; i++) {
+ if (virConnectClose(info->vp[i]) < 0)
+ ret = -errno;
+ }
+
+ free(info->vp);
+ free(info);
+ return ret;
+}
+
+
+static fence_callbacks_t libvirt_callbacks = {
+ .null = libvirt_null,
+ .off = libvirt_off,
+ .on = libvirt_on,
+ .reboot = libvirt_reboot,
+ .status = libvirt_status,
+ .devstatus = libvirt_devstatus,
+ .hostlist = libvirt_hostlist
+};
+
+static backend_plugin_t libvirt_plugin = {
+ .name = NAME,
+ .version = LIBVIRT_VERSION,
+ .callbacks = &libvirt_callbacks,
+ .init = libvirt_init,
+ .cleanup = libvirt_shutdown,
+};
+
+double
+BACKEND_VER_SYM(void)
+{
+ return PLUGIN_VERSION_BACKEND;
+}
+
+const backend_plugin_t *
+BACKEND_INFO_SYM(void)
+{
+ return &libvirt_plugin;
+}
diff --git a/agents/virt/server/main.c b/agents/virt/server/main.c
new file mode 100644
index 0000000..fe57899
--- /dev/null
+++ b/agents/virt/server/main.c
@@ -0,0 +1,281 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <syslog.h>
+
+/* Local includes */
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "xvm.h"
+#include "server_plugin.h"
+#include "simple_auth.h"
+#include "debug.h"
+
+/* configure.c */
+int daemon_init(const char *prog, const char *pid_file, int nofork);
+int daemon_cleanup(void);
+
+
+static void
+usage(void)
+{
+ printf("Usage: fence_virtd [options]\n");
+ printf(" -F Do not daemonize.\n");
+ printf(" -f <file> Use <file> as configuration file.\n");
+ printf(" -d <level> Set debugging level to <level>.\n");
+ printf(" -c Configuration mode.\n");
+ printf(" -l List plugins.\n");
+ printf(" -w Wait for initialization.\n");
+ printf(" -p <file> Use <file> to record the active process id.\n");
+}
+
+
+static int run = 1;
+static void
+exit_handler(int sig)
+{
+ run = 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ char val[4096];
+ char listener_name[80];
+ char backend_name[80];
+ const char *config_file = SYSCONFDIR "/fence_virt.conf";
+ char *pid_file = NULL;
+ config_object_t *config = NULL;
+ map_object_t *map = NULL;
+ const listener_plugin_t *lp;
+ const backend_plugin_t *p;
+ listener_context_t listener_ctx = NULL;
+ backend_context_t backend_ctx = NULL;
+ int debug_set = 0, foreground = 0, wait_for_init = 0;
+ int opt, configure = 0;
+
+ config = sc_init();
+ map = map_init();
+
+ if (!config || !map) {
+ perror("malloc");
+ return -1;
+ }
+
+ while ((opt = getopt(argc, argv, "Ff:d:cwlhp:")) != EOF) {
+ switch(opt) {
+ case 'F':
+ printf("Background mode disabled\n");
+ foreground = 1;
+ break;
+ case 'f':
+ printf("Using %s\n", optarg);
+ config_file = optarg;
+ break;
+ case 'p':
+ printf("Using %s\n", optarg);
+ pid_file = optarg;
+ break;
+ case 'd':
+ debug_set = atoi(optarg);
+ break;
+ case 'c':
+ configure = 1;
+ break;
+ case 'w':
+ wait_for_init = 1;
+ break;
+ case 'l':
+ plugin_dump();
+ return 0;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ default:
+ return -1;
+ }
+ }
+
+ if (configure) {
+ return do_configure(config, config_file);
+ }
+
+ if (sc_parse(config, config_file) != 0) {
+ printf("Failed to parse %s\n", config_file);
+ return -1;
+ }
+
+ if (debug_set) {
+ snprintf(val, sizeof(val), "%d", debug_set);
+ sc_set(config, "fence_virtd/@debug", val);
+ } else {
+ if (sc_get(config, "fence_virtd/@debug", val, sizeof(val))==0)
+ debug_set = atoi(val);
+ }
+
+ dset(debug_set);
+
+ if (!foreground) {
+ if (sc_get(config, "fence_virtd/@foreground",
+ val, sizeof(val)) == 0)
+ foreground = atoi(val);
+ }
+
+ if (!wait_for_init) {
+ if (sc_get(config, "fence_virtd/@wait_for_init",
+ val, sizeof(val)) == 0)
+ wait_for_init = atoi(val);
+ if (!wait_for_init) {
+ /* XXX compat */
+ if (sc_get(config, "fence_virtd/@wait_for_backend",
+ val, sizeof(val)) == 0)
+ wait_for_init = atoi(val);
+ }
+ }
+
+ if (dget() > 3)
+ sc_dump(config, stdout);
+
+ if (sc_get(config, "fence_virtd/@backend", backend_name,
+ sizeof(backend_name))) {
+ printf("Failed to determine backend.\n");
+ printf("%s\n", val);
+ return -1;
+ }
+
+ dbg_printf(1, "Backend plugin: %s\n", backend_name);
+
+ if (sc_get(config, "fence_virtd/@listener", listener_name,
+ sizeof(listener_name))) {
+ printf("Failed to determine backend.\n");
+ printf("%s\n", val);
+ return -1;
+ }
+
+ dbg_printf(1, "Listener plugin: %s\n", listener_name);
+
+ if (sc_get(config, "fence_virtd/@module_path", val,
+ sizeof(val))) {
+#ifdef MODULE_PATH
+ snprintf(val, sizeof(val), MODULE_PATH);
+#else
+ printf("Failed to determine module path.\n");
+ return -1;
+#endif
+ }
+
+ dbg_printf(1, "Searching %s for plugins...\n", val);
+
+ opt = plugin_search(val);
+ if (opt > 0) {
+ dbg_printf(1, "%d plugins found\n", opt);
+ } else {
+ printf("No plugins found\n");
+ return 1;
+ }
+
+ if (dget() > 3)
+ plugin_dump();
+
+ lp = plugin_find_listener(listener_name);
+ if (!lp) {
+ printf("Could not find listener \"%s\"\n", listener_name);
+ return 1;
+ }
+
+ p = plugin_find_backend(backend_name);
+ if (!p) {
+ printf("Could not find backend \"%s\"\n", backend_name);
+ return 1;
+ }
+
+ if (pid_file == NULL) {
+ pid_file = malloc(PATH_MAX);
+ memset(pid_file, 0, PATH_MAX);
+ snprintf(pid_file, PATH_MAX, "/var/run/%s.pid", basename(argv[0]));
+ }
+
+ if (check_file_permissions(config_file) != 0)
+ return -1;
+
+ sprintf(val, "listeners/%s/@key_file", listener_name);
+ if (sc_get(config, val,
+ val, sizeof(val)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", val);
+ } else {
+ snprintf(val, sizeof(val), "%s", DEFAULT_KEY_FILE);
+ }
+
+ if (check_file_permissions(val) != 0)
+ return -1;
+
+ openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON);
+
+ daemon_init(basename(argv[0]), pid_file, foreground);
+
+ signal(SIGINT, exit_handler);
+ signal(SIGTERM, exit_handler);
+ signal(SIGQUIT, exit_handler);
+
+ syslog(LOG_NOTICE, "fence_virtd starting. Listener: %s Backend: %s",
+ listener_name, backend_name);
+
+ while (p->init(&backend_ctx, config) < 0) {
+ if (!wait_for_init || !run) {
+ if (foreground) {
+ printf("Backend plugin %s failed to initialize\n",
+ backend_name);
+ }
+ syslog(LOG_ERR,
+ "Backend plugin %s failed to initialize\n",
+ backend_name);
+ return 1;
+ }
+ sleep(5);
+ }
+
+ if (map_load(map, config) < 0) {
+ syslog(LOG_WARNING, "Failed to load static maps\n");
+ }
+
+ /* only client we have now is mcast (fence_xvm behavior) */
+ while (lp->init(&listener_ctx, p->callbacks, config, map,
+ backend_ctx) != 0) {
+ if (!wait_for_init || !run) {
+ if (foreground) {
+ printf("Listener plugin %s failed to initialize\n",
+ listener_name);
+ }
+ syslog(LOG_ERR,
+ "Listener plugin %s failed to initialize\n",
+ listener_name);
+ return 1;
+ }
+ sleep(5);
+ }
+
+ while (run && lp->dispatch(listener_ctx, NULL) >= 0);
+
+ syslog(LOG_NOTICE, "fence_virtd shutting down");
+
+ map_release(map);
+ sc_release(config);
+
+ lp->cleanup(listener_ctx);
+ p->cleanup(backend_ctx);
+
+ plugin_unload();
+ daemon_cleanup();
+
+ return 0;
+}
diff --git a/agents/virt/server/mcast.c b/agents/virt/server/mcast.c
new file mode 100644
index 0000000..a95ab37
--- /dev/null
+++ b/agents/virt/server/mcast.c
@@ -0,0 +1,622 @@
+/*
+ Copyright Red Hat, Inc. 2006
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <nss.h>
+#include <libgen.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "debug.h"
+#include "fdops.h"
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+
+#define NAME "multicast"
+#define MCAST_VERSION "1.3"
+
+#define MCAST_MAGIC 0xabb911a3
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != MCAST_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+typedef struct _mcast_options {
+ char *addr;
+ char *key_file;
+ int ifindex;
+ int family;
+ unsigned int port;
+ unsigned int hash;
+ unsigned int auth;
+ unsigned int flags;
+} mcast_options;
+
+
+typedef struct _mcast_info {
+ uint64_t magic;
+ void *priv;
+ map_object_t *map;
+ history_info_t *history;
+ char key[MAX_KEY_LEN];
+ mcast_options args;
+ const fence_callbacks_t *cb;
+ ssize_t key_len;
+ int mc_sock;
+ int need_kill;
+} mcast_info;
+
+
+struct mcast_hostlist_arg {
+ map_object_t *map;
+ const char *src;
+ int fd;
+};
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ fence_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+connect_tcp(fence_req_t *req, fence_auth_type_t auth,
+ void *key, size_t key_len)
+{
+ int fd = -1;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ char buf[128];
+
+ switch(req->family) {
+ case PF_INET:
+ memset(&sin, 0, sizeof(sin));
+ memcpy(&sin.sin_addr, req->address,
+ sizeof(sin.sin_addr));
+ sin.sin_family = PF_INET;
+ fd = ipv4_connect(&sin.sin_addr, req->port,
+ 5);
+ if (fd < 0) {
+ printf("Failed to call back\n");
+ return -1;
+ }
+ break;
+ case PF_INET6:
+ memset(&sin6, 0, sizeof(sin6));
+ memcpy(&sin6.sin6_addr, req->address,
+ sizeof(sin6.sin6_addr));
+ sin.sin_family = PF_INET6;
+ fd = ipv6_connect(&sin6.sin6_addr, req->port,
+ 5);
+
+ memset(buf,0,sizeof(buf));
+ inet_ntop(PF_INET6, &sin6.sin6_addr, buf, sizeof(buf));
+
+ if (fd < 0) {
+ printf("Failed to call back %s\n", buf);
+ return -1;
+ }
+ break;
+ default:
+ printf("Family = %d\n", req->family);
+ return -1;
+ }
+
+ /* Noops if auth == AUTH_NONE */
+ if (sock_response(fd, auth, key, key_len, 10) <= 0) {
+ printf("Failed to respond to challenge\n");
+ close(fd);
+ return -1;
+ }
+
+ if (sock_challenge(fd, auth, key, key_len, 10) <= 0) {
+ printf("Remote failed challenge\n");
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+
+static int
+mcast_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct mcast_hostlist_arg *arg = (struct mcast_hostlist_arg *)priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+mcast_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ char val = (char)RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &val, 1, &tv);
+}
+
+
+static int
+mcast_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+do_fence_request_tcp(fence_req_t *req, mcast_info *info)
+{
+ char ip_addr_src[1024];
+ int fd = -1;
+ char response = 1;
+ struct mcast_hostlist_arg arg;
+
+ fd = connect_tcp(req, info->args.auth, info->key, info->key_len);
+ if (fd < 0) {
+ dbg_printf(2, "Could not send reply to fence request: %s\n",
+ strerror(errno));
+ goto out;
+ }
+
+ inet_ntop(req->family, req->address,
+ ip_addr_src, sizeof(ip_addr_src));
+
+ dbg_printf(2, "Request %d seqno %d src %s target %s\n",
+ req->request, req->seqno, ip_addr_src, req->domain);
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->map;
+ arg.src = ip_addr_src;
+ arg.fd = fd;
+
+ mcast_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(mcast_hostlist, &arg,
+ info->priv);
+ mcast_hostlist_end(arg.fd);
+ break;
+ }
+
+ dbg_printf(3, "Sending response to caller...\n");
+ if (_write_retry(fd, &response, 1, NULL) < 0) {
+ perror("write");
+ }
+
+ /* XVM shotguns multicast packets, so we want to avoid
+ * acting on the same request multiple times if the first
+ * attempt was successful.
+ */
+ history_record(info->history, req);
+out:
+ if (fd != -1)
+ close(fd);
+
+ return 1;
+}
+
+
+static int
+mcast_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ mcast_info *info;
+ fence_req_t data;
+ fd_set rfds;
+ struct sockaddr_in sin;
+ int len;
+ int n;
+ socklen_t slen;
+
+ info = (mcast_info *)c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ FD_SET(info->mc_sock, &rfds);
+
+ n = select((info->mc_sock)+1, &rfds, NULL, NULL, timeout);
+ if (n <= 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+ slen = sizeof(sin);
+ len = recvfrom(info->mc_sock, &data, sizeof(data), 0,
+ (struct sockaddr *)&sin, &slen);
+
+ if (len <= 0) {
+ perror("recvfrom");
+ return len;
+ }
+
+ swab_fence_req_t(&data);
+
+ if (!verify_request(&data, info->args.hash, info->key,
+ info->key_len)) {
+ printf("Key mismatch; dropping packet\n");
+ return 0;
+ }
+
+ printf("Request %d seqno %d domain %s\n", data.request, data.seqno,
+ data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ printf("We just did this request; dropping packet\n");
+ return 0;
+ }
+
+ switch(info->args.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ printf("Plain TCP request\n");
+ do_fence_request_tcp(&data, info);
+ break;
+ default:
+ printf("XXX Unhandled authentication\n");
+ }
+
+ return 0;
+}
+
+
+static int
+mcast_config(config_object_t *config, mcast_options *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/multicast/@key_file",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", value);
+ args->key_file = strdup(value);
+ } else {
+ args->key_file = strdup(DEFAULT_KEY_FILE);
+ if (!args->key_file) {
+ dbg_printf(1, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ args->hash = DEFAULT_HASH;
+ if (sc_get(config, "listeners/multicast/@hash",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for hash\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = HASH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = HASH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = HASH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = HASH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported hash: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->auth = DEFAULT_AUTH;
+ if (sc_get(config, "listeners/multicast/@auth",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for auth\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->auth = AUTH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->auth = AUTH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->auth = AUTH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->auth = AUTH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported auth: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->family = PF_INET;
+ if (sc_get(config, "listeners/multicast/@family",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for family\n", value);
+ if (!strcasecmp(value, "ipv4")) {
+ args->family = PF_INET;
+ } else if (!strcasecmp(value, "ipv6")) {
+ args->family = PF_INET6;
+ } else {
+ dbg_printf(1, "Unsupported family: %s\n", value);
+ ++errors;
+ }
+ }
+
+ if (sc_get(config, "listeners/multicast/@address",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for address\n", value);
+ args->addr = strdup(value);
+ } else {
+ if (args->family == PF_INET) {
+ args->addr = strdup(IPV4_MCAST_DEFAULT);
+ } else {
+ args->addr = strdup(IPV6_MCAST_DEFAULT);
+ }
+ }
+ if (!args->addr) {
+ return -1;
+ }
+
+ args->port = DEFAULT_MCAST_PORT;
+ if (sc_get(config, "listeners/multicast/@port",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for port\n", value);
+ args->port = atoi(value);
+ if (args->port <= 0) {
+ dbg_printf(1, "Invalid port: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->ifindex = 0;
+ if (sc_get(config, "listeners/multicast/@interface",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for interface\n", value);
+ args->ifindex = if_nametoindex(value);
+ if (args->ifindex < 0) {
+ dbg_printf(1, "Invalid interface: %s\n", value);
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+
+static int
+mcast_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ mcast_info *info;
+ int mc_sock, ret;
+
+ /* Initialize NSS; required to do hashing, as silly as that
+ sounds... */
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ printf("Could not initialize NSS\n");
+ return 1;
+ }
+
+ info = malloc(sizeof(*info));
+ if (!info)
+ return -1;
+ memset(info, 0, sizeof(*info));
+
+ info->priv = priv;
+ info->cb = cb;
+ info->map = map;
+
+ ret = mcast_config(config, &info->args);
+ if (ret < 0) {
+ perror("mcast_config");
+ free(info);
+ return -1;
+ } else if (ret > 0) {
+ printf("%d errors found during configuration\n",ret);
+ free(info);
+ return -1;
+ }
+
+ if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
+ info->key_len = read_key_file(info->args.key_file,
+ info->key, sizeof(info->key));
+ if (info->key_len < 0) {
+ printf("Could not read %s; operating without "
+ "authentication\n", info->args.key_file);
+ info->args.auth = AUTH_NONE;
+ info->args.hash = HASH_NONE;
+ info->key_len = 0;
+ }
+ }
+
+ if (info->args.family == PF_INET)
+ mc_sock = ipv4_recv_sk(info->args.addr,
+ info->args.port,
+ info->args.ifindex);
+ else
+ mc_sock = ipv6_recv_sk(info->args.addr,
+ info->args.port,
+ info->args.ifindex);
+ if (mc_sock < 0) {
+ printf("Could not set up multicast listen socket\n");
+ free(info);
+ return -1;
+ }
+
+ info->magic = MCAST_MAGIC;
+ info->mc_sock = mc_sock;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ return 0;
+}
+
+
+static int
+mcast_shutdown(listener_context_t c)
+{
+ mcast_info *info = (mcast_info *)c;
+
+ VALIDATE(info);
+ info->magic = 0;
+ history_wipe(info->history);
+ free(info->history);
+ free(info->args.key_file);
+ free(info->args.addr);
+ close(info->mc_sock);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t mcast_plugin = {
+ .name = NAME,
+ .version = MCAST_VERSION,
+ .init = mcast_init,
+ .dispatch = mcast_dispatch,
+ .cleanup = mcast_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &mcast_plugin;
+}
diff --git a/agents/virt/server/plugin.c b/agents/virt/server/plugin.c
new file mode 100644
index 0000000..d9a69fb
--- /dev/null
+++ b/agents/virt/server/plugin.c
@@ -0,0 +1,417 @@
+/*
+ Copyright Red Hat, Inc. 2002-2004, 2009
+
+ The Magma Cluster API Library is free software; you can redistribute
+ it and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either version
+ 2.1 of the License, or (at your option) any later version.
+
+ The Magma Cluster API Library is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+ */
+/** @file
+ * Plugin loading routines
+ */
+
+#include "config.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <string.h>
+#include <dirent.h>
+
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "debug.h"
+
+typedef struct _plugin_list {
+ list_head();
+ const listener_plugin_t *listener;
+ const backend_plugin_t *backend;
+ void *handle;
+ plugin_type_t type;
+} plugin_list_t;
+
+static plugin_list_t *server_plugins = NULL;
+
+
+static int
+plugin_reg_backend(void *handle, const backend_plugin_t *plugin)
+{
+ plugin_list_t *newplug;
+
+ if (plugin_find_backend(plugin->name)) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ newplug = malloc(sizeof(*newplug));
+ if (!newplug)
+ return -1;
+ memset(newplug, 0, sizeof(*newplug));
+ newplug->backend = plugin;
+ newplug->type = PLUGIN_BACKEND;
+ newplug->handle = handle;
+
+ list_insert(&server_plugins, newplug);
+ return 0;
+}
+
+
+static int
+plugin_reg_listener(void *handle, const listener_plugin_t *plugin)
+{
+ plugin_list_t *newplug;
+
+ if (plugin_find_listener(plugin->name)) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ newplug = malloc(sizeof(*newplug));
+ if (!newplug)
+ return -1;
+ memset(newplug, 0, sizeof(*newplug));
+ newplug->listener = plugin;
+ newplug->type = PLUGIN_LISTENER;
+ newplug->handle = handle;
+
+ list_insert(&server_plugins, newplug);
+ return 0;
+}
+
+
+void
+plugin_dump(void)
+{
+ plugin_list_t *p;
+ int x, y;
+
+ y = 0;
+ list_for(&server_plugins, p, x) {
+ if (p->type == PLUGIN_BACKEND) {
+ if (!y) {
+ y = 1;
+ printf("Available backends:\n");
+ }
+ printf(" %s %s\n",
+ p->backend->name, p->backend->version);
+ }
+ }
+
+ y = 0;
+ list_for(&server_plugins, p, x) {
+ if (p->type == PLUGIN_LISTENER) {
+ if (!y) {
+ y = 1;
+ printf("Available listeners:\n");
+ }
+ printf(" %s %s\n",
+ p->listener->name, p->listener->version);
+ }
+ }
+}
+
+
+const backend_plugin_t *
+plugin_find_backend(const char *name)
+{
+ plugin_list_t *p;
+ int x;
+
+ list_for(&server_plugins, p, x) {
+ if (p->type != PLUGIN_BACKEND)
+ continue;
+ if (!strcasecmp(name, p->backend->name))
+ return p->backend;
+ }
+
+ return NULL;
+}
+
+
+const listener_plugin_t *
+plugin_find_listener(const char *name)
+{
+ plugin_list_t *p;
+ int x;
+
+ list_for(&server_plugins, p, x) {
+ if (p->type != PLUGIN_LISTENER)
+ continue;
+ if (!strcasecmp(name, p->listener->name))
+ return p->listener;
+ }
+
+ return NULL;
+}
+
+
+static int
+backend_plugin_load(void *handle, const char *libpath)
+{
+ const backend_plugin_t *plug = NULL;
+ double (*modversion)(void);
+ backend_plugin_t *(*modinfo)(void);
+
+ modversion = dlsym(handle, BACKEND_VER_STR);
+ if (!modversion) {
+ dbg_printf(1, "Failed to map %s\n", BACKEND_VER_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (modversion() != PLUGIN_VERSION_BACKEND) {
+ dbg_printf(1, "API version mismatch in %s: \n"
+ " %f expected; %f received.\n", libpath,
+ PLUGIN_VERSION_BACKEND, modversion());
+ errno = EINVAL;
+ return -1;
+ }
+
+ modinfo = dlsym(handle, BACKEND_INFO_STR);
+ if (!modinfo) {
+ dbg_printf(1, "Failed to map %s\n", BACKEND_INFO_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ plug = modinfo();
+ if (plugin_reg_backend(handle, plug) < 0) {
+ dbg_printf(1, "Failed to register %s %s\n", plug->name,
+ plug->version);
+ errno = EINVAL;
+ return -1;
+ } else {
+ dbg_printf(1, "Registered backend plugin %s %s\n",
+ plug->name, plug->version);
+ }
+
+ return 0;
+}
+
+
+static int
+listener_plugin_load(void *handle, const char *libpath)
+{
+ const listener_plugin_t *plug = NULL;
+ double (*modversion)(void);
+ listener_plugin_t *(*modinfo)(void);
+
+ modversion = dlsym(handle, LISTENER_VER_STR);
+ if (!modversion) {
+ dbg_printf(1, "Failed to map %s\n", LISTENER_VER_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (modversion() != PLUGIN_VERSION_LISTENER) {
+ dbg_printf(1, "API version mismatch in %s: \n"
+ " %f expected; %f received.\n", libpath,
+ PLUGIN_VERSION_LISTENER, modversion());
+ dlclose(handle);
+ errno = EINVAL;
+ return -1;
+ }
+
+ modinfo = dlsym(handle, LISTENER_INFO_STR);
+ if (!modinfo) {
+ dbg_printf(1, "Failed to map %s\n", LISTENER_INFO_STR);
+ errno = EINVAL;
+ return -1;
+ }
+
+ plug = modinfo();
+ if (plugin_reg_listener(handle, plug) < 0) {
+ dbg_printf(1, "Failed to register %s %s\n", plug->name,
+ plug->version);
+ errno = EINVAL;
+ return -1;
+ } else {
+ dbg_printf(1, "Registered listener plugin %s %s\n",
+ plug->name, plug->version);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Load a cluster plugin .so file and map all the functions
+ * provided to entries in a backend_plugin_t structure.
+ *
+ * @param libpath Path to file.
+ * @return NULL on failure, or plugin-specific
+ * (const) backend_plugin_t * structure on
+ * success.
+ */
+int
+plugin_load(const char *libpath)
+{
+ void *handle = NULL;
+
+ errno = 0;
+
+ if (!libpath) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ dbg_printf(3, "Loading plugin from %s\n", libpath);
+ handle = dlopen(libpath, RTLD_NOW);
+ if (!handle) {
+ dbg_printf(3, "Could not dlopen %s: %s\n", libpath, dlerror());
+ errno = ELIBACC;
+ return -1;
+ }
+
+ if (!backend_plugin_load(handle, libpath) ||
+ !listener_plugin_load(handle, libpath))
+ return 0;
+
+ dbg_printf(3, "%s is not a valid plugin\n", libpath);
+ dlclose(handle);
+ errno = EINVAL;
+ return -1;
+}
+
+void
+plugin_unload(void)
+{
+ plugin_list_t *p;
+ int x;
+
+ list_for(&server_plugins, p, x) {
+ dlclose(p->handle);
+ }
+}
+
+/**
+ Free up a null-terminated array of strings
+ */
+static void
+free_dirnames(char **dirnames)
+{
+ int x = 0;
+
+ for (; dirnames[x]; x++)
+ free(dirnames[x]);
+
+ free(dirnames);
+}
+
+
+static int
+_compare(const void *a, const void *b)
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+
+/**
+ Read all entries in a directory and return them in a NULL-terminated,
+ sorted array.
+ */
+static int
+read_dirnames_sorted(const char *directory, char ***dirnames)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char filename[1024];
+ int count = 0, x = 0;
+
+ dir = opendir(directory);
+ if (!dir)
+ return -1;
+
+ /* Count the number of plugins */
+ while ((entry = readdir(dir)) != NULL)
+ ++count;
+
+ /* Malloc the entries */
+ *dirnames = malloc(sizeof(char *) * (count+1));
+ if (!*dirnames) {
+#ifdef DEBUG
+ printf("%s: Failed to malloc %d bytes",
+ __FUNCTION__, (int)(sizeof(char *) * (count+1)));
+#endif
+ closedir(dir);
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(*dirnames, 0, sizeof(char *) * (count + 1));
+ rewinddir(dir);
+
+ /* Store the directory names. */
+ while ((entry = readdir(dir)) != NULL) {
+ snprintf(filename, sizeof(filename), "%s/%s", directory,
+ entry->d_name);
+
+ (*dirnames)[x] = strdup(filename);
+ if (!(*dirnames)[x]) {
+#ifdef DEBUG
+ printf("Failed to duplicate %s\n",
+ filename);
+#endif
+ free_dirnames(*dirnames);
+ closedir(dir);
+ errno = ENOMEM;
+ return -1;
+ }
+ ++x;
+ }
+
+ closedir(dir);
+
+ /* Sort the directory names. */
+ qsort((*dirnames), count, sizeof(char *), _compare);
+
+ return 0;
+}
+
+
+/**
+ */
+int
+plugin_search(const char *pathname)
+{
+ int found = 0;
+ int fcount = 0;
+ char **filenames;
+
+ dbg_printf(1, "Searching for plugins in %s\n", pathname);
+ if (read_dirnames_sorted(pathname, &filenames) != 0) {
+ return -1;
+ }
+
+ for (fcount = 0; filenames[fcount]; fcount++) {
+
+ if (plugin_load(filenames[fcount]) == 0)
+ ++found;
+ }
+
+ free_dirnames(filenames);
+ if (!found) {
+ dbg_printf(1, "No usable plugins found.\n");
+ errno = ELIBACC;
+ return -1;
+ }
+
+ return found;
+}
diff --git a/agents/virt/server/serial.c b/agents/virt/server/serial.c
new file mode 100644
index 0000000..bde8218
--- /dev/null
+++ b/agents/virt/server/serial.c
@@ -0,0 +1,459 @@
+/*
+ Copyright Red Hat, Inc. 2010
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <nss.h>
+#include <libgen.h>
+
+/* Local includes */
+#include "debug.h"
+#include "fdops.h"
+#include "serial.h"
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+#include "xvm.h"
+
+#define NAME "serial"
+#define SERIAL_VERSION "0.5"
+
+#define SERIAL_PLUG_MAGIC 0x1227a000
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != SERIAL_PLUG_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+
+typedef struct _serial_info {
+ uint64_t magic;
+ const fence_callbacks_t *cb;
+ void *priv;
+ char *uri;
+ char *path;
+ history_info_t *history;
+ map_object_t *maps;
+ int mode;
+ int wake_fd;
+} serial_info;
+
+
+struct serial_hostlist_arg {
+ map_object_t *map;
+ const char *src;
+ int fd;
+};
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ serial_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+serial_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct serial_hostlist_arg *arg = (struct serial_hostlist_arg *)priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+serial_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ serial_resp_t resp;
+
+ resp.magic = SERIAL_MAGIC;
+ resp.response = RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &resp, sizeof(resp), &tv);
+}
+
+
+static int
+serial_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ //printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+do_fence_request(int fd, const char *src, serial_req_t *req, serial_info *info)
+{
+ char response = RESP_FAIL;
+ struct serial_hostlist_arg arg;
+ serial_resp_t resp;
+
+ arg.fd = fd;
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->maps, src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->maps;
+ arg.src = src;
+ arg.fd = fd;
+
+ serial_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(serial_hostlist, &arg,
+ info->priv);
+ serial_hostlist_end(arg.fd);
+ break;
+ }
+
+ resp.magic = SERIAL_MAGIC;
+ resp.response = response;
+ swab_serial_resp_t(&resp);
+
+ dbg_printf(3, "Sending response to caller...\n");
+ if (_write_retry(fd, &resp, sizeof(resp), NULL) < 0)
+ perror("write");
+
+ /* XVM shotguns multicast packets, so we want to avoid
+ * acting on the same request multiple times if the first
+ * attempt was successful.
+ */
+ history_record(info->history, req);
+
+ return 1;
+}
+
+
+static int
+serial_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ char src_domain[MAX_DOMAINNAME_LENGTH];
+ serial_info *info;
+ serial_req_t data;
+ fd_set rfds;
+ struct timeval tv;
+ int max;
+ int n, x, ret;
+
+ info = (serial_info *)c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ domain_sock_fdset(&rfds, &max);
+ FD_SET(info->wake_fd, &rfds);
+ if (info->wake_fd > max)
+ max = info->wake_fd;
+
+ n = select(max+1, &rfds, NULL, NULL, timeout);
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+ /*
+ * See if the goal was just to be woken up in order to refill our
+ * file descriptor set. For example, if multiple domains were
+ * created simultaneously, we would have to refill our fd_set
+ */
+ if (FD_ISSET(info->wake_fd, &rfds)) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ _read_retry(info->wake_fd, &c, 1, &tv);
+ return 0;
+ }
+
+ /*
+ * If no requests, we're done
+ */
+ if (n == 0)
+ return 0;
+
+ /* find & read request */
+ for (x = 0; x <= max; x++) {
+ if (FD_ISSET(x, &rfds)) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ ret = _read_retry(x, &data, sizeof(data), &tv);
+
+ if (ret != sizeof(data)) {
+ if (--n > 0)
+ continue;
+ else
+ return 0;
+ } else {
+ swab_serial_req_t(&data);
+ break;
+ }
+ }
+ }
+
+ src_domain[0] = 0;
+ domain_sock_name(x, src_domain, sizeof(src_domain));
+
+ dbg_printf(2, "Sock %d Request %d seqno %d src %s target %s\n", x,
+ data.request, data.seqno, src_domain, data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ dbg_printf(3, "We just did this request; dropping packet\n");
+ return 0;
+ }
+
+ do_fence_request(x, src_domain[0] == 0 ? NULL : src_domain,
+ &data, info);
+
+ return 0;
+}
+
+
+static int
+serial_config(config_object_t *config, serial_info *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/serial/@uri",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for uri\n", value);
+ args->uri = strdup(value);
+ }
+
+ if (sc_get(config, "listeners/serial/@path",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for path\n", value);
+ args->path = strdup(value);
+ }
+
+ if (sc_get(config, "listeners/serial/@mode",
+ value, sizeof(value)-1) == 0) {
+ if (!strcasecmp(value, "vmchannel")) {
+ args->mode = 1;
+ } else if (!strcasecmp(value, "serial")) {
+ args->mode = 0;
+ } else {
+ args->mode = atoi(value);
+ if (args->mode < 0)
+ args->mode = 0;
+ }
+
+ dbg_printf(1, "Got %s for mode\n",
+ args->mode?"VMChannel":"serial");
+
+ }
+
+ return errors;
+}
+
+
+static int
+serial_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ serial_info *info;
+ int ret;
+
+ info = malloc(sizeof(*info));
+ if (!info)
+ return -1;
+ memset(info, 0, sizeof(*info));
+
+ info->priv = priv;
+ info->cb = cb;
+
+ ret = serial_config(config, info);
+ if (ret < 0) {
+ perror("serial_config");
+ return -1;
+ } else if (ret > 0) {
+ printf("%d errors found during configuration\n",ret);
+ return -1;
+ }
+
+ info->maps = map;
+
+ info->magic = SERIAL_PLUG_MAGIC;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ start_event_listener(info->uri, info->path, info->mode, &info->wake_fd);
+ sleep(1);
+
+ return 0;
+}
+
+
+static int
+serial_shutdown(listener_context_t c)
+{
+ serial_info *info = (serial_info *)c;
+
+ dbg_printf(3, "Shutting down serial\n");
+
+ VALIDATE(info);
+ info->magic = 0;
+ stop_event_listener();
+ domain_sock_cleanup();
+ history_wipe(info->history);
+ free(info->history);
+ free(info->uri);
+ free(info->path);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t serial_plugin = {
+ .name = NAME,
+ .version = SERIAL_VERSION,
+ .init = serial_init,
+ .dispatch = serial_dispatch,
+ .cleanup = serial_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &serial_plugin;
+}
diff --git a/agents/virt/server/serial.h b/agents/virt/server/serial.h
new file mode 100644
index 0000000..481400a
--- /dev/null
+++ b/agents/virt/server/serial.h
@@ -0,0 +1,20 @@
+#ifndef __VIRT_SERIAL_H
+#define __VIRT_SERIAL_H
+
+#include <sys/select.h>
+
+/* virt-sockets.c */
+int domain_sock_setup(const char *domain, const char *socket_path);
+int domain_sock_close(const char *domain);
+int domain_sock_fdset(fd_set *set, int *max);
+
+/* Find the domain name associated with a FD */
+int domain_sock_name(int fd, char *outbuf, size_t buflen);
+int domain_sock_cleanup(void);
+
+/* virt-serial.c - event thread control functions */
+int start_event_listener(const char *uri, const char *path, int mode, int *wake_fd);
+int stop_event_listener(void);
+
+
+#endif
diff --git a/agents/virt/server/static_map.c b/agents/virt/server/static_map.c
new file mode 100644
index 0000000..7bdc400
--- /dev/null
+++ b/agents/virt/server/static_map.c
@@ -0,0 +1,237 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "list.h"
+#include "debug.h"
+#include "serial.h"
+#include "uuid-test.h"
+
+struct perm_entry {
+ list_head();
+ char name[129];
+};
+
+struct perm_group {
+ list_head();
+ struct perm_entry *uuids;
+ struct perm_entry *ips;
+ char name[129];
+};
+
+
+static void
+static_map_cleanup(void **info)
+{
+ struct perm_group *groups = (struct perm_group *)(*info);
+ struct perm_group *group;
+ struct perm_entry *entry;
+
+ while (groups) {
+ group = groups;
+ list_remove(&groups, group);
+ while (group->uuids) {
+ entry = group->uuids;
+ list_remove(&group->uuids, entry);
+ free(entry);
+ }
+ while (group->ips) {
+ entry = group->ips;
+ list_remove(&group->ips, entry);
+ free(entry);
+ }
+ free(group);
+ }
+
+ *info = NULL;
+}
+
+
+static int
+static_map_check(void *info, const char *src, const char *tgt_uuid, const char *tgt_name)
+{
+ struct perm_group *groups = (struct perm_group *)info;
+ struct perm_group *group;
+ struct perm_entry *left, *tmp;
+ int x, y, uuid = 0;
+
+ if (!info)
+ return 1; /* no maps == wide open */
+
+ dbg_printf(99, "[server:map_check] map request: src: %s uuid: %s name: %s\n", src, tgt_uuid, tgt_name);
+
+ uuid = is_uuid(src);
+
+ list_for(&groups, group, x) {
+ left = NULL;
+
+ if (uuid) {
+ list_for(&group->uuids, tmp, y) {
+ if (!strcasecmp(tmp->name, src)) {
+ left = tmp;
+ break;
+ }
+ }
+ } else {
+ list_for(&group->ips, tmp, y) {
+ if (!strcasecmp(tmp->name, src)) {
+ left = tmp;
+ break;
+ }
+ }
+ }
+
+ if (!left)
+ continue;
+
+ list_for(&group->uuids, tmp, y) {
+ if (!strcasecmp(tmp->name, tgt_uuid)) {
+ return 1;
+ }
+ /* useful only for list */
+ if (tgt_name) {
+ if (!strcasecmp(tmp->name, tgt_name)) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+static_map_load(void *config_ptr, void **perm_info)
+{
+ config_object_t *config = config_ptr;
+ int group_idx = 0;
+ int entry_idx = 0;
+ int found;
+ char value[128];
+ char buf[256];
+ char buf2[512];
+ struct perm_group *group = NULL, *groups = NULL;
+ struct perm_entry *entry = NULL;
+
+ if (!perm_info)
+ return -1;
+
+ do {
+ snprintf(buf, sizeof(buf)-1, "groups/group[%d]", ++group_idx);
+
+ if (sc_get(config, buf, value, sizeof(value)) != 0) {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@uuid", buf);
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@ip", buf);
+ if (sc_get(config, buf2, value,
+ sizeof(value)) != 0) {
+ break;
+ }
+ }
+ snprintf(buf2, sizeof(buf2)-1, "%s/@name", buf);
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ snprintf(value, sizeof(value), "unnamed-%d",
+ group_idx);
+ }
+ }
+
+ group = malloc(sizeof(*group));
+ assert(group);
+ memset(group, 0, sizeof(*group));
+ strncpy(group->name, value, sizeof(group->name));
+ dbg_printf(3, "Group: %s\n", value);
+
+ found = 0;
+ entry_idx = 0;
+ do {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@uuid[%d]",
+ buf, ++entry_idx);
+
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ break;
+ }
+
+ ++found;
+ entry = malloc(sizeof(*entry));
+ assert(entry);
+ memset(entry, 0, sizeof(*entry));
+ strncpy(entry->name, value, sizeof(entry->name));
+ dbg_printf(3, " - UUID Entry: %s\n", value);
+
+ list_insert(&group->uuids, entry);
+
+ } while (1);
+
+ entry_idx = 0;
+ do {
+ snprintf(buf2, sizeof(buf2)-1, "%s/@ip[%d]",
+ buf, ++entry_idx);
+
+ if (sc_get(config, buf2, value, sizeof(value)) != 0) {
+ break;
+ }
+
+ ++found;
+ entry = malloc(sizeof(*entry));
+ assert(entry);
+ memset(entry, 0, sizeof(*entry));
+ strncpy(entry->name, value, sizeof(entry->name));
+ dbg_printf(3, " - IP Entry: %s\n", value);
+
+ list_insert(&group->ips, entry);
+
+ } while (1);
+
+
+ if (!found)
+ free(group);
+ else
+ list_insert(&groups, group);
+
+ } while (1);
+
+ *perm_info = groups;
+
+ return 0;
+}
+
+
+static const map_object_t static_map_obj = {
+ .load = static_map_load,
+ .check = static_map_check,
+ .cleanup = static_map_cleanup,
+ .info = NULL
+};
+
+
+void *
+map_init(void)
+{
+ map_object_t *o;
+
+ o = malloc(sizeof(*o));
+ if (!o)
+ return NULL;
+ memset(o, 0, sizeof(*o));
+ memcpy(o, &static_map_obj, sizeof(*o));
+
+ return (void *)o;
+}
+
+
+void
+map_release(void *c)
+{
+ map_object_t *o = (map_object_t *)c;
+
+ static_map_cleanup(&o->info);
+ free(c);
+}
diff --git a/agents/virt/server/tcp.c b/agents/virt/server/tcp.c
new file mode 100644
index 0000000..c1fb60c
--- /dev/null
+++ b/agents/virt/server/tcp.c
@@ -0,0 +1,575 @@
+/*
+ Copyright Red Hat, Inc. 2006-2012
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <nss.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "tcp_listener.h"
+#include "debug.h"
+#include "fdops.h"
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+
+#define NAME "tcp"
+#define TCP_VERSION "0.2"
+
+#define TCP_MAGIC 0xc3dff7a9
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != TCP_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+typedef struct _tcp_options {
+ char *key_file;
+ char *addr;
+ int family;
+ unsigned int port;
+ unsigned int hash;
+ unsigned int auth;
+ unsigned int flags;
+} tcp_options;
+
+
+typedef struct _tcp_info {
+ uint64_t magic;
+ void *priv;
+ map_object_t *map;
+ history_info_t *history;
+ char key[MAX_KEY_LEN];
+ tcp_options args;
+ const fence_callbacks_t *cb;
+ ssize_t key_len;
+ int listen_sock;
+} tcp_info;
+
+
+struct tcp_hostlist_arg {
+ map_object_t *map;
+ const char *src;
+ int fd;
+};
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ fence_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+tcp_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct tcp_hostlist_arg *arg = (struct tcp_hostlist_arg *)priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+tcp_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ char val = (char)RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &val, 1, &tv);
+}
+
+
+static int
+tcp_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+static socklen_t
+sockaddr_len(const struct sockaddr_storage *ss)
+{
+ if (ss->ss_family == AF_INET) {
+ return sizeof(struct sockaddr_in);
+ } else {
+ return sizeof(struct sockaddr_in6);
+ }
+}
+
+static int
+do_fence_request_tcp(int fd, struct sockaddr_storage *ss, socklen_t sock_len, fence_req_t *req, tcp_info *info)
+{
+ char ip_addr_src[1024];
+ char response = 1;
+ struct tcp_hostlist_arg arg;
+ int ret;
+
+ /* Noops if auth == AUTH_NONE */
+ if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) {
+ printf("Failed to respond to challenge\n");
+ close(fd);
+ return -1;
+ }
+
+ ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10);
+ if (ret <= 0) {
+ printf("Remote failed challenge\n");
+ close(fd);
+ return -1;
+ }
+
+
+ if (getnameinfo((struct sockaddr *)ss, sockaddr_len(ss),
+ ip_addr_src, sizeof(ip_addr_src),
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ printf("Unable to resolve!\n");
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(2, "Request %d seqno %d src %s target %s\n",
+ req->request, req->seqno, ip_addr_src, req->domain);
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, ip_addr_src,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->map, ip_addr_src,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->map;
+ arg.src = ip_addr_src;
+ arg.fd = fd;
+
+ tcp_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(tcp_hostlist, &arg,
+ info->priv);
+ tcp_hostlist_end(arg.fd);
+ break;
+ }
+
+ dbg_printf(3, "Sending response to caller...\n");
+ if (_write_retry(fd, &response, 1, NULL) < 0) {
+ perror("write");
+ }
+
+ history_record(info->history, req);
+
+ if (fd != -1)
+ close(fd);
+
+ return 1;
+}
+
+
+static int
+tcp_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ tcp_info *info;
+ fence_req_t data;
+ fd_set rfds;
+ int n;
+ int client_fd;
+ int ret;
+ struct timeval tv;
+ struct sockaddr_storage ss;
+ socklen_t sock_len = sizeof(ss);
+
+ if (timeout != NULL)
+ memcpy(&tv, timeout, sizeof(tv));
+ else {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ }
+
+ info = (tcp_info *)c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ FD_SET(info->listen_sock, &rfds);
+
+ n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout);
+ if (n <= 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+ client_fd = accept(info->listen_sock, (struct sockaddr *)&ss, &sock_len);
+ if (client_fd < 0) {
+ perror("accept");
+ return -1;
+ }
+
+ dbg_printf(3, "Accepted client...\n");
+
+ ret = _read_retry(client_fd, &data, sizeof(data), &tv);
+ if (ret != sizeof(data)) {
+ dbg_printf(3, "Invalid request (read %d bytes)\n", ret);
+ close(client_fd);
+ return 0;
+ }
+
+ swab_fence_req_t(&data);
+
+ if (!verify_request(&data, info->args.hash, info->key,
+ info->key_len)) {
+ printf("Key mismatch; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ dbg_printf(3, "Request %d seqno %d domain %s\n",
+ data.request, data.seqno, data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ printf("We just did this request; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ switch(info->args.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ printf("Plain TCP request\n");
+ do_fence_request_tcp(client_fd, &ss, sock_len, &data, info);
+ break;
+ default:
+ printf("XXX Unhandled authentication\n");
+ }
+
+ return 0;
+}
+
+
+static int
+tcp_config(config_object_t *config, tcp_options *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/tcp/@key_file",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", value);
+ args->key_file = strdup(value);
+ } else {
+ args->key_file = strdup(DEFAULT_KEY_FILE);
+ if (!args->key_file) {
+ dbg_printf(1, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ args->hash = DEFAULT_HASH;
+ if (sc_get(config, "listeners/tcp/@hash",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for hash\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = HASH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = HASH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = HASH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = HASH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported hash: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->auth = DEFAULT_AUTH;
+ if (sc_get(config, "listeners/tcp/@auth",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for auth\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = AUTH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = AUTH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = AUTH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = AUTH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported auth: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->family = PF_INET;
+ if (sc_get(config, "listeners/tcp/@family",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for family\n", value);
+ if (!strcasecmp(value, "ipv4")) {
+ args->family = PF_INET;
+ } else if (!strcasecmp(value, "ipv6")) {
+ args->family = PF_INET6;
+ } else {
+ dbg_printf(1, "Unsupported family: %s\n", value);
+ ++errors;
+ }
+ }
+
+ if (sc_get(config, "listeners/tcp/@address",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for address\n", value);
+ args->addr = strdup(value);
+ } else {
+ if (args->family == PF_INET) {
+ args->addr = strdup(IPV4_TCP_ADDR_DEFAULT);
+ } else {
+ args->addr = strdup(IPV6_TCP_ADDR_DEFAULT);
+ }
+ }
+ if (!args->addr) {
+ return -1;
+ }
+
+ args->port = DEFAULT_MCAST_PORT;
+ if (sc_get(config, "listeners/tcp/@port",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for port\n", value);
+ args->port = atoi(value);
+ if (args->port <= 0) {
+ dbg_printf(1, "Invalid port: %s\n", value);
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+
+static int
+tcp_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ tcp_info *info;
+ int listen_sock, ret;
+
+ /* Initialize NSS; required to do hashing, as silly as that
+ sounds... */
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ printf("Could not initialize NSS\n");
+ return 1;
+ }
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+
+ info->priv = priv;
+ info->cb = cb;
+ info->map = map;
+
+ ret = tcp_config(config, &info->args);
+ if (ret < 0)
+ perror("tcp_config");
+ else if (ret > 0)
+ printf("%d errors found during configuration\n",ret);
+
+ if (ret != 0) {
+ if (info->args.key_file)
+ free(info->args.key_file);
+ if (info->args.addr)
+ free(info->args.addr);
+ free(info);
+ return -1;
+ }
+
+ if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
+ info->key_len = read_key_file(info->args.key_file,
+ info->key, sizeof(info->key));
+ if (info->key_len < 0) {
+ printf("Could not read %s; operating without "
+ "authentication\n", info->args.key_file);
+ info->args.auth = AUTH_NONE;
+ info->args.hash = HASH_NONE;
+ info->key_len = 0;
+ }
+ }
+
+ if (info->args.family == PF_INET) {
+ listen_sock = ipv4_listen(info->args.addr, info->args.port, 10);
+ } else {
+ listen_sock = ipv6_listen(info->args.addr, info->args.port, 10);
+ }
+
+ if (listen_sock < 0) {
+ printf("Could not set up listen socket\n");
+ if (info->args.key_file)
+ free(info->args.key_file);
+ if (info->args.addr)
+ free(info->args.addr);
+ free(info);
+ return -1;
+ }
+
+ info->magic = TCP_MAGIC;
+ info->listen_sock = listen_sock;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ return 0;
+}
+
+
+static int
+tcp_shutdown(listener_context_t c)
+{
+ tcp_info *info = (tcp_info *)c;
+
+ VALIDATE(info);
+ info->magic = 0;
+ history_wipe(info->history);
+ free(info->history);
+ free(info->args.key_file);
+ free(info->args.addr);
+ close(info->listen_sock);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t tcp_plugin = {
+ .name = NAME,
+ .version = TCP_VERSION,
+ .init = tcp_init,
+ .dispatch = tcp_dispatch,
+ .cleanup = tcp_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &tcp_plugin;
+}
diff --git a/agents/virt/server/uuid-test.c b/agents/virt/server/uuid-test.c
new file mode 100644
index 0000000..3116ef9
--- /dev/null
+++ b/agents/virt/server/uuid-test.c
@@ -0,0 +1,66 @@
+#include "config.h"
+
+#include <uuid/uuid.h>
+#include <errno.h>
+#include <string.h>
+
+#include "uuid-test.h"
+
+int
+is_uuid(const char *value)
+{
+ uuid_t id;
+ char test_value[37];
+
+ if (strlen(value) < 36) {
+ return 0;
+ }
+
+ memset(id, 0, sizeof(uuid_t));
+
+ if (uuid_is_null(id) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (uuid_parse(value, id) < 0) {
+ return 0;
+ }
+
+ memset(test_value, 0, sizeof(test_value));
+ uuid_unparse(id, test_value);
+
+ if (strcasecmp(value, test_value)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef STANDALONE
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+ int ret;
+
+ if (argc < 2) {
+ printf("Usage: uuidtest <value>\n");
+ return 1;
+ }
+
+ ret = is_uuid(argv[1]);
+ if (ret == 0) {
+ printf("%s is NOT a uuid\n", argv[1]);
+ } else if (ret == 1) {
+ printf("%s is a uuid\n", argv[1]);
+ } else {
+ printf("Error: %s\n", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/agents/virt/server/uuid-test.h b/agents/virt/server/uuid-test.h
new file mode 100644
index 0000000..164fec7
--- /dev/null
+++ b/agents/virt/server/uuid-test.h
@@ -0,0 +1,14 @@
+#ifndef __UUID_TEST_H
+#define __UUID_TEST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int is_uuid(const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/agents/virt/server/virt-serial.c b/agents/virt/server/virt-serial.c
new file mode 100644
index 0000000..6b369bc
--- /dev/null
+++ b/agents/virt/server/virt-serial.c
@@ -0,0 +1,444 @@
+// #include <config.h>
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <libvirt/libvirt.h>
+
+#include <libxml/xmlreader.h>
+
+#include "simpleconfig.h"
+#include "debug.h"
+
+#define DEBUG0(fmt) dbg_printf(5,"%s:%d :: " fmt "\n", \
+ __func__, __LINE__)
+#define DEBUG1(fmt, ...) dbg_printf(5, "%s:%d: " fmt "\n", \
+ __func__, __LINE__, __VA_ARGS__)
+
+#include "serial.h"
+
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+
+static pthread_t event_tid = 0;
+static int run = 0;
+
+/* Prototypes */
+const char *eventToString(int event);
+int myDomainEventCallback1(virConnectPtr conn, virDomainPtr dom,
+ int event, int detail, void *opaque);
+
+void usage(const char *pname);
+
+struct domain_info {
+ virDomainPtr dom;
+ virDomainEventType event;
+};
+
+static int
+is_in_directory(const char *dir, const char *pathspec)
+{
+ char *last_slash = NULL;
+ size_t dirlen, pathlen;
+
+ if (!dir || !pathspec)
+ return 0;
+
+ dirlen = strlen(dir);
+ pathlen = strlen(pathspec);
+
+ /*
+ printf("dirlen = %d pathlen = %d\n",
+ dirlen, pathlen);
+ */
+
+ /* chop off trailing slashes */
+ while (dirlen && dir[dirlen-1]=='/')
+ --dirlen;
+
+ /* chop off leading slashes */
+ while (dirlen && dir[0] == '/') {
+ ++dir;
+ --dirlen;
+ }
+
+ /* chop off leading slashes */
+ while (pathlen && pathspec[0] == '/') {
+ ++pathspec;
+ --pathlen;
+ }
+
+ if (!dirlen || !pathlen)
+ return 0;
+
+ if (pathlen <= dirlen)
+ return 0;
+
+ last_slash = strrchr(pathspec, '/');
+
+ if (!last_slash)
+ return 0;
+
+ while (*last_slash == '/' && last_slash > pathspec)
+ --last_slash;
+
+ if (last_slash == pathspec)
+ return 0;
+
+ pathlen = last_slash - pathspec + 1;
+ /*printf("real dirlen = %d real pathlen = %d\n",
+ dirlen, pathlen);*/
+ if (pathlen != dirlen)
+ return 0;
+
+ /* todo - intelligently skip multiple slashes mid-path */
+ return !strncmp(dir, pathspec, dirlen);
+}
+
+
+static int
+domainStarted(virDomainPtr mojaDomain, const char *path, int mode)
+{
+ char dom_uuid[42];
+ char *xml;
+ xmlDocPtr doc;
+ xmlNodePtr cur, devices, child, serial;
+ xmlAttrPtr attr, attr_mode, attr_path;
+
+ if (!mojaDomain)
+ return -1;
+
+ virDomainGetUUIDString(mojaDomain, dom_uuid);
+
+ xml = virDomainGetXMLDesc(mojaDomain, 0);
+ // printf("%s\n", xml);
+ // @todo: free mojaDomain
+
+ // parseXML output
+ doc = xmlParseMemory(xml, strlen(xml));
+ xmlFree(xml);
+ cur = xmlDocGetRootElement(doc);
+
+ if (cur == NULL) {
+ fprintf(stderr, "Empty doc\n");
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ if (xmlStrcmp(cur->name, (const xmlChar *) "domain")) {
+ fprintf(stderr, "no domain?\n");
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ devices = cur->xmlChildrenNode;
+ for (devices = cur->xmlChildrenNode; devices != NULL;
+ devices = devices->next) {
+ if (xmlStrcmp(devices->name, (const xmlChar *) "devices")) {
+ continue;
+ }
+
+ for (child = devices->xmlChildrenNode; child != NULL;
+ child = child->next) {
+
+ if ((!mode && xmlStrcmp(child->name, (const xmlChar *) "serial")) ||
+ (mode && xmlStrcmp(child->name, (const xmlChar *) "channel"))) {
+ continue;
+ }
+
+ attr = xmlHasProp(child, (const xmlChar *)"type");
+ if (attr == NULL)
+ continue;
+
+ if (xmlStrcmp(attr->children->content,
+ (const xmlChar *) "unix")) {
+ continue;
+ }
+
+ for (serial = child->xmlChildrenNode; serial != NULL;
+ serial = serial->next) {
+ if (xmlStrcmp(serial->name,
+ (const xmlChar *) "source")) {
+ continue;
+ }
+
+ attr_mode = xmlHasProp(serial, (const xmlChar *)"mode");
+ attr_path = xmlHasProp(serial, (const xmlChar *)"path");
+
+ if (!attr_path || !attr_mode)
+ continue;
+
+ if (xmlStrcmp(attr_mode->children->content,
+ (const xmlChar *) "bind"))
+ continue;
+
+ if (path && !is_in_directory(path, (const char *)
+ attr_path->children->content))
+ continue;
+
+ domain_sock_setup(dom_uuid, (const char *)
+ attr_path->children->content);
+ }
+ }
+ }
+
+ xmlFreeDoc(doc);
+ return 0;
+}
+
+static int
+registerExisting(virConnectPtr vp, const char *path, int mode)
+{
+ int *d_ids = NULL;
+ int d_count, x;
+ virDomainPtr dom;
+ virDomainInfo d_info;
+
+ errno = EINVAL;
+ if (!vp)
+ return -1;
+
+ d_count = virConnectNumOfDomains(vp);
+ if (d_count <= 0) {
+ if (d_count == 0) {
+ /* Successful, but no domains running */
+ errno = 0;
+ return 0;
+ }
+ goto out_fail;
+ }
+
+ d_ids = malloc(sizeof (int) * d_count);
+ if (!d_ids)
+ goto out_fail;
+
+ if (virConnectListDomains(vp, d_ids, d_count) < 0)
+ goto out_fail;
+
+ /* Ok, we have the domain IDs - let's get their names and states */
+ for (x = 0; x < d_count; x++) {
+ dom = virDomainLookupByID(vp, d_ids[x]);
+ if (!dom) {
+ /* XXX doom */
+ goto out_fail;
+ }
+
+ if (virDomainGetInfo(dom, &d_info) < 0) {
+ /* XXX no info for the domain?!! */
+ virDomainFree(dom);
+ goto out_fail;
+ }
+
+ if (d_info.state != VIR_DOMAIN_SHUTOFF &&
+ d_info.state != VIR_DOMAIN_CRASHED)
+ domainStarted(dom, path, mode);
+
+ virDomainFree(dom);
+ }
+
+ out_fail:
+ free(d_ids);
+ return 0;
+}
+
+static int
+domainStopped(virDomainPtr mojaDomain)
+{
+ char dom_uuid[42];
+
+ if (!mojaDomain)
+ return -1;
+
+ virDomainGetUUIDString(mojaDomain, dom_uuid);
+ domain_sock_close(dom_uuid);
+
+ return 0;
+}
+
+
+struct event_args {
+ char *uri;
+ char *path;
+ int mode;
+ int wake_fd;
+};
+
+static void
+connectClose(virConnectPtr conn ATTRIBUTE_UNUSED,
+ int reason,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ switch (reason) {
+ case VIR_CONNECT_CLOSE_REASON_ERROR:
+ dbg_printf(2, "Connection closed due to I/O error\n");
+ break;
+ case VIR_CONNECT_CLOSE_REASON_EOF:
+ dbg_printf(2, "Connection closed due to end of file\n");
+ break;
+ case VIR_CONNECT_CLOSE_REASON_KEEPALIVE:
+ dbg_printf(2, "Connection closed due to keepalive timeout\n");
+ break;
+ case VIR_CONNECT_CLOSE_REASON_CLIENT:
+ dbg_printf(2, "Connection closed due to client request\n");
+ break;
+ default:
+ dbg_printf(2, "Connection closed due to unknown reason\n");
+ break;
+ };
+ run = 0;
+}
+
+int
+myDomainEventCallback1(virConnectPtr conn,
+ virDomainPtr dom, int event, int detail, void *opaque)
+{
+ struct event_args *args = (struct event_args *)opaque;
+
+ if (event == VIR_DOMAIN_EVENT_STARTED ||
+ event == VIR_DOMAIN_EVENT_STOPPED) {
+ virDomainRef(dom);
+ if (event == VIR_DOMAIN_EVENT_STARTED) {
+ domainStarted(dom, args->path, args->mode);
+ virDomainFree(dom);
+ if (write(args->wake_fd, "x", 1) != 1) {
+ dbg_printf(3, "Unable to wake up thread\n");
+ }
+ } else if (event == VIR_DOMAIN_EVENT_STOPPED) {
+ domainStopped(dom);
+ virDomainFree(dom);
+ }
+ }
+
+ return 0;
+}
+
+
+static void *
+event_thread(void *arg)
+{
+ struct event_args *args = (struct event_args *)arg;
+ virConnectPtr dconn = NULL;
+ int callback1ret = -1;
+
+ dbg_printf(3, "Libvirt event listener starting\n");
+ if (args->uri)
+ dbg_printf(3," * URI: %s\n", args->uri);
+ if (args->path)
+ dbg_printf(3," * Socket path: %s\n", args->path);
+ dbg_printf(3," * Mode: %s\n", args->mode ? "VMChannel" : "Serial");
+
+ if (virEventRegisterDefaultImpl() < 0) {
+ dbg_printf(1, "Failed to register default event impl\n");
+ goto out;
+ }
+
+ dconn = virConnectOpen(args->uri);
+ if (!dconn) {
+ dbg_printf(1, "Error connecting to libvirt\n");
+ goto out;
+ }
+
+ virConnectRegisterCloseCallback(dconn, connectClose, NULL, NULL);
+
+ DEBUG0("Registering domain event cbs");
+
+ registerExisting(dconn, args->path, args->mode);
+
+ callback1ret =
+ virConnectDomainEventRegister(dconn, myDomainEventCallback1, arg, NULL);
+
+ if (callback1ret != -1) {
+ if (virConnectSetKeepAlive(dconn, 5, 5) < 0) {
+ dbg_printf(1, "Failed to start keepalive protocol\n");
+ run = 0;
+ }
+ while (run) {
+ if (virEventRunDefaultImpl() < 0) {
+ dbg_printf(1, "RunDefaultImpl Failed\n");
+ }
+ }
+
+ DEBUG0("Deregistering event handlers");
+ virConnectDomainEventDeregister(dconn, myDomainEventCallback1);
+ }
+
+ DEBUG0("Closing connection");
+ if (dconn && virConnectClose(dconn) < 0) {
+ dbg_printf(1, "error closing libvirt connection\n");
+ }
+
+out:
+ free(args->uri);
+ free(args->path);
+ free(args);
+ return NULL;
+}
+
+
+int
+start_event_listener(const char *uri, const char *path, int mode, int *wake_fd)
+{
+ struct event_args *args = NULL;
+ int wake_pipe[2];
+
+ virInitialize();
+
+ args = malloc(sizeof(*args));
+ if (!args)
+ return -1;
+ memset(args, 0, sizeof(*args));
+
+ if (pipe2(wake_pipe, O_CLOEXEC) < 0) {
+ goto out_fail;
+ }
+
+ if (uri) {
+ args->uri = strdup(uri);
+ if (args->uri == NULL)
+ goto out_fail;
+ }
+
+ if (path) {
+ args->path = strdup(path);
+ if (args->path == NULL)
+ goto out_fail;
+ }
+
+ args->mode = mode;
+ //args->p_tid = pthread_self();
+ *wake_fd = wake_pipe[0];
+ args->wake_fd = wake_pipe[1];
+
+ run = 1;
+
+ return pthread_create(&event_tid, NULL, event_thread, args);
+
+out_fail:
+ free(args->uri);
+ free(args->path);
+ free(args);
+ return -1;
+}
+
+
+int
+stop_event_listener(void)
+{
+ run = 0;
+ //pthread_cancel(event_tid);
+ pthread_join(event_tid, NULL);
+ event_tid = 0;
+
+ return 0;
+}
+
+
diff --git a/agents/virt/server/virt-sockets.c b/agents/virt/server/virt-sockets.c
new file mode 100644
index 0000000..7f36f62
--- /dev/null
+++ b/agents/virt/server/virt-sockets.c
@@ -0,0 +1,242 @@
+#include "config.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <list.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "serial.h"
+#include "debug.h"
+#include "simpleconfig.h"
+
+struct socket_list {
+ list_head();
+ char *domain_name;
+ char *socket_path;
+ int socket_fd;
+};
+
+static struct socket_list *socks = NULL;
+static pthread_mutex_t sock_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+static int
+connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout)
+{
+ int ret, flags, err;
+ unsigned l;
+ fd_set rfds, wfds;
+ struct timeval tv;
+
+ /*
+ Set up non-blocking connect
+ */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ return -1;
+ }
+
+ ret = connect(fd, dest, len);
+
+ if ((ret < 0) && (errno != EINPROGRESS))
+ return -1;
+
+ if (ret == 0)
+ goto done;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (!FD_ISSET(fd, &rfds) && !FD_ISSET(fd, &wfds)) {
+ errno = EIO;
+ return -1;
+ }
+
+ l = sizeof(err);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR,
+ (void *)&err, &l) < 0) {
+ return -1;
+ }
+
+ if (err != 0) {
+ errno = err;
+ return -1;
+ }
+
+done:
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+
+int
+domain_sock_setup(const char *domain, const char *socket_path)
+{
+ struct sockaddr_un *sun = NULL;
+ struct socket_list *node = NULL;
+ socklen_t sun_len;
+ int sock = -1;
+
+ sun_len = sizeof(*sun) + strlen(socket_path) + 1;
+ sun = malloc(sun_len);
+ if (!sun)
+ return -1;
+
+ memset((char *)sun, 0, sun_len);
+ sun->sun_family = PF_LOCAL;
+ strncpy(sun->sun_path, socket_path, sizeof(sun->sun_path) - 1);
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0)
+ goto out_fail;
+
+ if (connect_nb(sock, (struct sockaddr *)sun, SUN_LEN(sun), 3) < 0)
+ goto out_fail;
+
+ free(sun);
+ sun = NULL;
+
+ node = calloc(1, sizeof(*node));
+ if (!node)
+ goto out_fail;
+
+ node->domain_name = strdup(domain);
+ if (!node->domain_name)
+ goto out_fail;
+
+ node->socket_path = strdup(socket_path);
+ if (!node->socket_path)
+ goto out_fail;
+
+ node->socket_fd = sock;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_insert(&socks, node);
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ dbg_printf(3, "Registered %s on %d\n", domain, sock);
+ return 0;
+
+out_fail:
+ if (node) {
+ free(node->domain_name);
+ if (node->socket_path)
+ free(node->socket_path);
+ free(node);
+ }
+ free(sun);
+ if (sock >= 0)
+ close(sock);
+ return -1;
+}
+
+
+int
+domain_sock_close(const char *domain)
+{
+ struct socket_list *node = NULL;
+ struct socket_list *dead = NULL;
+ int x;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_for(&socks, node, x) {
+ if (!strcasecmp(domain, node->domain_name)) {
+ list_remove(&socks, node);
+ dead = node;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ if (dead) {
+ dbg_printf(3, "Unregistered %s, fd%d\n",
+ dead->domain_name,
+ dead->socket_fd);
+ close(dead->socket_fd);
+ free(dead->domain_name);
+ free(dead->socket_path);
+ free(dead);
+ }
+
+ return 0;
+}
+
+
+int
+domain_sock_fdset(fd_set *fds, int *max)
+{
+ struct socket_list *node = NULL;
+ int x = 0, _max = -1;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_for(&socks, node, x) {
+ FD_SET(node->socket_fd, fds);
+ if (node->socket_fd > _max)
+ _max = node->socket_fd;
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ if (max)
+ *max = _max;
+
+ return x;
+}
+
+
+int
+domain_sock_name(int fd, char *outbuf, size_t buflen)
+{
+ struct socket_list *node = NULL;
+ int ret = 1, x = 0;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ list_for(&socks, node, x) {
+ if (node->socket_fd == fd) {
+ snprintf(outbuf, buflen, "%s", node->domain_name);
+ ret = 0;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ return ret;
+}
+
+
+int
+domain_sock_cleanup(void)
+{
+ struct socket_list *dead= NULL;
+
+ pthread_mutex_lock(&sock_list_mutex);
+ while(socks) {
+ dead = socks;
+ list_remove(&socks, dead);
+ close(dead->socket_fd);
+ free(dead->domain_name);
+ free(dead->socket_path);
+ free(dead);
+ }
+ pthread_mutex_unlock(&sock_list_mutex);
+
+ return 0;
+}
+
diff --git a/agents/virt/server/virt.c b/agents/virt/server/virt.c
new file mode 100644
index 0000000..d4c94e9
--- /dev/null
+++ b/agents/virt/server/virt.c
@@ -0,0 +1,630 @@
+/*
+ Copyright Red Hat, Inc. 2006-2017
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <libvirt/libvirt.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "debug.h"
+#include "uuid-test.h"
+#include "virt.h"
+
+static int
+_compare_virt(const void *_left, const void *_right)
+{
+ virt_state_t *left = (virt_state_t *)_left,
+ *right = (virt_state_t *)_right;
+
+ return strcasecmp(left->v_name, right->v_name);
+}
+
+
+static void
+_free_dom_list(virDomainPtr *dom_list, int len) {
+ int x;
+
+ if (!dom_list || len <= 0)
+ return;
+ for (x = 0 ; x < len; x++)
+ virDomainFree(dom_list[x]);
+
+ free(dom_list);
+}
+
+
+virt_list_t *vl_get(virConnectPtr *vp, int vp_count, int my_id)
+{
+ virt_list_t *vl = NULL;
+ int d_count = 0;
+ int i;
+
+ errno = EINVAL;
+ if (!vp || vp_count < 1)
+ return NULL;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ int x;
+ virDomainPtr *dom_list;
+ virt_list_t *new_vl;
+
+ int ret = virConnectListAllDomains(vp[i], &dom_list, 0);
+ if (ret == 0)
+ continue;
+
+ if (ret < 0) {
+ int saved_errno = errno;
+ dbg_printf(2, "Error: virConnectListAllDomains: %d %d\n",
+ ret, saved_errno);
+ if (vl)
+ free(vl);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ d_count += ret;
+ new_vl = realloc(vl, sizeof(uint32_t) + sizeof(virt_state_t) * d_count);
+ if (!new_vl) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+ vl = new_vl;
+ vl->vm_count = d_count;
+
+ /* Ok, we have the domain IDs - let's get their names and states */
+ for (x = 0; x < ret; x++) {
+ char *d_name;
+ virDomainInfo d_info;
+ char d_uuid[MAX_DOMAINNAME_LENGTH];
+ virDomainPtr dom = dom_list[x];
+
+ if (!(d_name = (char *)virDomainGetName(dom))) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+
+ if (virDomainGetUUIDString(dom, d_uuid) != 0) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+
+ if (virDomainGetInfo(dom, &d_info) < 0) {
+ _free_dom_list(dom_list, ret);
+ free(vl);
+ return NULL;
+ }
+
+ /* Store the name & state */
+ strncpy(vl->vm_states[x].v_name, d_name, MAX_DOMAINNAME_LENGTH);
+ strncpy(vl->vm_states[x].v_uuid, d_uuid, MAX_DOMAINNAME_LENGTH);
+ vl->vm_states[x].v_state.s_state = d_info.state;
+ vl->vm_states[x].v_state.s_owner = my_id;
+ }
+
+ _free_dom_list(dom_list, ret);
+ }
+ /* No domains found */
+ if (!vl)
+ return NULL;
+
+ /* We have all the locally running domains & states now */
+ /* Sort */
+ qsort(&vl->vm_states[0], vl->vm_count, sizeof(vl->vm_states[0]),
+ _compare_virt);
+ return vl;
+}
+
+int
+vl_add(virt_list_t **vl, virt_state_t *vm) {
+ virt_list_t *new_vl;
+ size_t oldlen;
+ size_t newlen;
+
+ if (!vl)
+ return -1;
+
+ if (!*vl) {
+ *vl = malloc(sizeof(uint32_t) + sizeof(virt_state_t));
+ if (!*vl)
+ return -1;
+ (*vl)->vm_count = 1;
+ memcpy(&(*vl)->vm_states[0], vm, sizeof(virt_state_t));
+ return 0;
+ }
+
+ oldlen = sizeof(uint32_t) + sizeof(virt_state_t) * (*vl)->vm_count;
+ newlen = oldlen + sizeof(virt_state_t);
+
+ new_vl = malloc(newlen);
+ if (!new_vl)
+ return -1;
+
+ memcpy(new_vl, *vl, oldlen);
+ memcpy(&new_vl->vm_states[(*vl)->vm_count], vm, sizeof(virt_state_t));
+ new_vl->vm_count++;
+
+ free(*vl);
+ *vl = new_vl;
+ return 0;
+}
+
+int vl_remove_by_owner(virt_list_t **vl, uint32_t owner) {
+ int i;
+ int removed = 0;
+ virt_list_t *new_vl;
+
+ if (!vl || !*vl)
+ return 0;
+
+ for (i = 0 ; i < (*vl)->vm_count ; i++) {
+ if ((*vl)->vm_states[i].v_state.s_owner == owner) {
+ dbg_printf(2, "Removing %s\n", (*vl)->vm_states[i].v_name);
+ memset(&(*vl)->vm_states[i].v_state, 0,
+ sizeof((*vl)->vm_states[i].v_state));
+ (*vl)->vm_states[i].v_name[0] = 0xff;
+ (*vl)->vm_states[i].v_uuid[0] = 0xff;
+ removed++;
+ }
+ }
+
+ if (!removed)
+ return 0;
+
+ qsort(&(*vl)->vm_states[0], (*vl)->vm_count, sizeof((*vl)->vm_states[0]),
+ _compare_virt);
+ (*vl)->vm_count -= removed;
+
+ new_vl = realloc(*vl, sizeof(uint32_t) + (sizeof(virt_state_t) * ((*vl)->vm_count)));
+ if (new_vl)
+ *vl = new_vl;
+ return removed;
+}
+
+
+int
+vl_update(virt_list_t **vl, virt_state_t *vm) {
+ virt_state_t *v = NULL;
+
+ if (!vl)
+ return -1;
+
+ if (!*vl)
+ return vl_add(vl, vm);
+
+ if (strlen(vm->v_uuid) > 0)
+ v = vl_find_uuid(*vl, vm->v_uuid);
+
+ if (v == NULL && strlen(vm->v_name) > 0)
+ v = vl_find_name(*vl, vm->v_name);
+
+ if (v == NULL) {
+ dbg_printf(2, "Adding new entry for VM %s\n", vm->v_name);
+ vl_add(vl, vm);
+ } else {
+ dbg_printf(2, "Updating entry for VM %s\n", vm->v_name);
+ memcpy(&v->v_state, &vm->v_state, sizeof(v->v_state));
+ }
+
+ return 0;
+}
+
+
+void
+vl_print(virt_list_t *vl)
+{
+ int x;
+
+ printf("%-24.24s %-36.36s %-5.5s %-5.5s\n", "Domain", "UUID",
+ "Owner", "State");
+ printf("%-24.24s %-36.36s %-5.5s %-5.5s\n", "------", "----",
+ "-----", "-----");
+
+ if (!vl || !vl->vm_count)
+ return;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ printf("%-24.24s %-36.36s %-5.5d %-5.5d\n",
+ vl->vm_states[x].v_name,
+ vl->vm_states[x].v_uuid,
+ vl->vm_states[x].v_state.s_owner,
+ vl->vm_states[x].v_state.s_state);
+ }
+}
+
+
+virt_state_t *
+vl_find_name(virt_list_t *vl, const char *name)
+{
+ int x;
+
+ if (!vl || !name || !vl->vm_count)
+ return NULL;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ if (!strcasecmp(vl->vm_states[x].v_name, name))
+ return &vl->vm_states[x];
+ }
+
+ return NULL;
+}
+
+
+virt_state_t *
+vl_find_uuid(virt_list_t *vl, const char *uuid)
+{
+ int x;
+
+ if (!vl || !uuid || !vl->vm_count)
+ return NULL;
+
+ for (x = 0; x < vl->vm_count; x++) {
+ if (!strcasecmp(vl->vm_states[x].v_uuid, uuid))
+ return &vl->vm_states[x];
+ }
+
+ return NULL;
+}
+
+
+void
+vl_free(virt_list_t *old)
+{
+ free(old);
+}
+
+
+static inline int
+wait_domain(const char *vm_name, virConnectPtr vp, int timeout)
+{
+ int tries = 0;
+ int response = 1;
+ int ret;
+ virDomainPtr vdp;
+ virDomainInfo vdi;
+ int uuid_check;
+
+ uuid_check = is_uuid(vm_name);
+
+ if (uuid_check) {
+ vdp = virDomainLookupByUUIDString(vp, (const char *)vm_name);
+ } else {
+ vdp = virDomainLookupByName(vp, vm_name);
+ }
+ if (!vdp)
+ return 0;
+
+ /* Check domain liveliness. If the domain is still here,
+ we return failure, and the client must then retry */
+ /* XXX On the xen 3.0.4 API, we will be able to guarantee
+ synchronous virDomainDestroy, so this check will not
+ be necessary */
+ do {
+ if (++tries > timeout)
+ break;
+
+ sleep(1);
+ if (uuid_check) {
+ vdp = virDomainLookupByUUIDString(vp, (const char *)vm_name);
+ } else {
+ vdp = virDomainLookupByName(vp, vm_name);
+ }
+ if (!vdp) {
+ dbg_printf(2, "Domain no longer exists\n");
+ response = 0;
+ break;
+ }
+
+ memset(&vdi, 0, sizeof(vdi));
+ ret = virDomainGetInfo(vdp, &vdi);
+ virDomainFree(vdp);
+ if (ret < 0)
+ continue;
+
+ if (vdi.state == VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "Domain has been shut off\n");
+ response = 0;
+ break;
+ }
+
+ dbg_printf(4, "Domain still exists (state %d) after %d seconds\n",
+ vdi.state, tries);
+ } while (1);
+
+ return response;
+}
+
+
+int
+vm_off(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL;
+ virDomainInfo vdi;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+ int ret = -1;
+ int i;
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp)
+ break;
+ }
+
+ if (!vdp) {
+ dbg_printf(2, "[virt:OFF] Domain %s does not exist\n", vm_name);
+ return 1;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF)
+ {
+ dbg_printf(2, "[virt:OFF] Nothing to do - "
+ "domain %s is already off\n",
+ vm_name);
+ virDomainFree(vdp);
+ return 0;
+ }
+
+ syslog(LOG_NOTICE, "Destroying domain %s\n", vm_name);
+ dbg_printf(2, "[virt:OFF] Calling virDomainDestroy for %s\n", vm_name);
+
+ ret = virDomainDestroy(vdp);
+ virDomainFree(vdp);
+
+ if (ret < 0) {
+ syslog(LOG_NOTICE,
+ "Failed to destroy domain %s: %d\n", vm_name, ret);
+ dbg_printf(2, "[virt:OFF] Failed to destroy domain: %s %d\n",
+ vm_name, ret);
+ return 1;
+ }
+
+ if (ret) {
+ syslog(LOG_NOTICE, "Domain %s still exists; fencing failed\n",
+ vm_name);
+ dbg_printf(2,
+ "[virt:OFF] Domain %s still exists; fencing failed\n",
+ vm_name);
+ return 1;
+ }
+
+ dbg_printf(2, "[virt:OFF] Success for %s\n", vm_name);
+ return 0;
+}
+
+
+int
+vm_on(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL;
+ virDomainInfo vdi;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+ int ret = -1;
+ int i;
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp)
+ break;
+ }
+
+ if (!vdp) {
+ dbg_printf(2, "[virt:ON] Domain %s does not exist\n", vm_name);
+ return 1;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state != VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "Nothing to do - domain %s is already running\n",
+ vm_name);
+ virDomainFree(vdp);
+ return 0;
+ }
+
+ syslog(LOG_NOTICE, "Starting domain %s\n", vm_name);
+ dbg_printf(2, "[virt:ON] Calling virDomainCreate for %s\n", vm_name);
+
+ ret = virDomainCreate(vdp);
+ virDomainFree(vdp);
+
+ if (ret < 0) {
+ syslog(LOG_NOTICE, "Failed to start domain %s: %d\n", vm_name, ret);
+ dbg_printf(2, "[virt:ON] virDomainCreate() failed for %s: %d\n",
+ vm_name, ret);
+ return 1;
+ }
+
+ if (ret) {
+ syslog(LOG_NOTICE, "Domain %s did not start\n", vm_name);
+ dbg_printf(2, "[virt:ON] Domain %s did not start\n", vm_name);
+ return 1;
+ }
+
+ syslog(LOG_NOTICE, "Domain %s started\n", vm_name);
+ dbg_printf(2, "[virt:ON] Success for %s\n", vm_name);
+ return 0;
+}
+
+
+int
+vm_status(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL;
+ virDomainInfo vdi;
+ int ret = 0;
+ int i;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp)
+ break;
+ }
+
+ if (!vdp) {
+ dbg_printf(2, "[virt:STATUS] Unknown VM %s - return OFF\n", vm_name);
+ return RESP_OFF;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "[virt:STATUS] VM %s is OFF\n", vm_name);
+ ret = RESP_OFF;
+ }
+
+ if (vdp)
+ virDomainFree(vdp);
+ return ret;
+}
+
+
+int
+vm_reboot(virConnectPtr *vp, int vp_count, const char *vm_name)
+{
+ virDomainPtr vdp = NULL, nvdp;
+ virDomainInfo vdi;
+ char *domain_desc;
+ virConnectPtr vcp = NULL;
+ virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *);
+ int ret;
+ int i;
+
+ if (is_uuid(vm_name))
+ virt_lookup_fn = virDomainLookupByUUIDString;
+ else
+ virt_lookup_fn = virDomainLookupByName;
+
+ for (i = 0 ; i < vp_count ; i++) {
+ vdp = virt_lookup_fn(vp[i], vm_name);
+ if (vdp) {
+ vcp = vp[i];
+ break;
+ }
+ }
+
+ if (!vdp || !vcp) {
+ dbg_printf(2,
+ "[virt:REBOOT] Nothing to do - domain %s does not exist\n",
+ vm_name);
+ return 1;
+ }
+
+ if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF) {
+ dbg_printf(2, "[virt:REBOOT] Nothing to do - domain %s is off\n",
+ vm_name);
+ virDomainFree(vdp);
+ return 0;
+ }
+
+ syslog(LOG_NOTICE, "Rebooting domain %s\n", vm_name);
+ dbg_printf(5, "[virt:REBOOT] Rebooting domain %s...\n", vm_name);
+
+ domain_desc = virDomainGetXMLDesc(vdp, 0);
+
+ if (!domain_desc) {
+ dbg_printf(5, "[virt:REBOOT] Failed getting domain description "
+ "from libvirt for %s...\n", vm_name);
+ }
+
+ dbg_printf(2, "[virt:REBOOT] Calling virDomainDestroy(%p) for %s\n",
+ vdp, vm_name);
+
+ ret = virDomainDestroy(vdp);
+ if (ret < 0) {
+ dbg_printf(2,
+ "[virt:REBOOT] virDomainDestroy() failed for %s: %d/%d\n",
+ vm_name, ret, errno);
+
+ if (domain_desc)
+ free(domain_desc);
+ virDomainFree(vdp);
+ return 1;
+ }
+
+ ret = wait_domain(vm_name, vcp, 15);
+
+ if (ret) {
+ syslog(LOG_NOTICE, "Domain %s still exists; fencing failed\n", vm_name);
+ dbg_printf(2,
+ "[virt:REBOOT] Domain %s still exists; fencing failed\n",
+ vm_name);
+
+ if (domain_desc)
+ free(domain_desc);
+ virDomainFree(vdp);
+ return 1;
+ }
+
+ if (!domain_desc)
+ return 0;
+
+ /* 'on' is not a failure */
+ ret = 0;
+
+ dbg_printf(3, "[[ XML Domain Info ]]\n");
+ dbg_printf(3, "%s\n[[ XML END ]]\n", domain_desc);
+
+ dbg_printf(2, "[virt:REBOOT] Calling virDomainCreateLinux() for %s\n",
+ vm_name);
+
+ nvdp = virDomainCreateLinux(vcp, domain_desc, 0);
+ if (nvdp == NULL) {
+ /* More recent versions of libvirt or perhaps the
+ * KVM back-end do not let you create a domain from
+ * XML if there is already a defined domain description
+ * with the same name that it knows about. You must
+ * then call virDomainCreate() */
+ dbg_printf(2,
+ "[virt:REBOOT] virDomainCreateLinux() failed for %s; "
+ "Trying virDomainCreate()\n",
+ vm_name);
+
+ if (virDomainCreate(vdp) < 0) {
+ syslog(LOG_NOTICE, "Could not restart %s\n", vm_name);
+ dbg_printf(1, "[virt:REBOOT] Failed to recreate guest %s!\n",
+ vm_name);
+ }
+ }
+
+ free(domain_desc);
+ virDomainFree(vdp);
+ return ret;
+}
diff --git a/agents/virt/server/virt.h b/agents/virt/server/virt.h
new file mode 100644
index 0000000..1d9140c
--- /dev/null
+++ b/agents/virt/server/virt.h
@@ -0,0 +1,62 @@
+/*
+ Copyright Red Hat, Inc. 2006-2017
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#ifndef _VIRT_H
+#define _VIRT_H
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <libvirt/libvirt.h>
+
+#include "xvm.h"
+
+typedef struct {
+ uint32_t s_owner;
+ int32_t s_state;
+} vm_state_t;
+
+typedef struct {
+ char v_name[MAX_DOMAINNAME_LENGTH + 1];
+ char v_uuid[MAX_DOMAINNAME_LENGTH + 1];
+ vm_state_t v_state;
+} virt_state_t;
+
+/**
+ This is stored in our private checkpoint section.
+ */
+typedef struct _virt_list {
+ uint32_t vm_count;
+ virt_state_t vm_states[0];
+} virt_list_t;
+
+virt_list_t *vl_get(virConnectPtr *vp, int vp_count, int my_id);
+void vl_print(virt_list_t *vl);
+void vl_free(virt_list_t *old);
+virt_state_t *vl_find_uuid(virt_list_t *vl, const char *name);
+virt_state_t *vl_find_name(virt_list_t *vl, const char *name);
+int vl_add(virt_list_t **vl, virt_state_t *vm);
+int vl_update(virt_list_t **vl, virt_state_t *vm);
+int vl_remove_by_owner(virt_list_t **vl, uint32_t owner);
+
+int vm_off(virConnectPtr *vp, int vp_count, const char *vm_name);
+int vm_on(virConnectPtr *vp, int vp_count, const char *vm_name);
+int vm_status(virConnectPtr *vp, int vp_count, const char *vm_name);
+int vm_reboot(virConnectPtr *vp, int vp_count, const char *vm_name);
+
+#endif
diff --git a/agents/virt/server/vsock.c b/agents/virt/server/vsock.c
new file mode 100644
index 0000000..1b88fa5
--- /dev/null
+++ b/agents/virt/server/vsock.c
@@ -0,0 +1,565 @@
+/*
+ Copyright Red Hat, Inc. 2017
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+#include "config.h"
+
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <nss.h>
+#include <sys/socket.h>
+#include <linux/vm_sockets.h>
+
+/* Local includes */
+#include "list.h"
+#include "simpleconfig.h"
+#include "static_map.h"
+#include "server_plugin.h"
+#include "history.h"
+#include "xvm.h"
+#include "simple_auth.h"
+#include "options.h"
+#include "mcast.h"
+#include "tcp.h"
+#include "tcp_listener.h"
+#include "debug.h"
+#include "fdops.h"
+
+#define NAME "vsock"
+#define VSOCK_VERSION "0.2"
+
+#define VSOCK_MAGIC 0xa32d27c1e
+
+#define VALIDATE(info) \
+do {\
+ if (!info || info->magic != VSOCK_MAGIC)\
+ return -EINVAL;\
+} while(0)
+
+typedef struct _vsock_options {
+ char *key_file;
+ int cid;
+ unsigned int port;
+ unsigned int hash;
+ unsigned int auth;
+ unsigned int flags;
+} vsock_options;
+
+
+typedef struct _vsock_info {
+ uint64_t magic;
+ void *priv;
+ map_object_t *map;
+ history_info_t *history;
+ char key[MAX_KEY_LEN];
+ vsock_options args;
+ const fence_callbacks_t *cb;
+ ssize_t key_len;
+ int listen_sock;
+} vsock_info;
+
+
+struct vsock_hostlist_arg {
+ map_object_t *map;
+ int cid;
+ int fd;
+};
+
+
+static int get_peer_cid(int fd, uint32_t *peer_cid) {
+ struct sockaddr_vm svm;
+ socklen_t len;
+ int ret;
+
+ if (!peer_cid)
+ return -1;
+
+ len = sizeof(svm);
+ ret = getpeername(fd, (struct sockaddr *) &svm, &len);
+ if (ret < 0) {
+ printf("Error getting peer CID: %s\n", strerror(errno));
+ return -1;
+ }
+
+ *peer_cid = svm.svm_cid;
+ return 0;
+}
+
+
+/*
+ * See if we fenced this node recently (successfully)
+ * If so, ignore the request for a few seconds.
+ *
+ * We purge our history when the entries time out.
+ */
+static int
+check_history(void *a, void *b) {
+ fence_req_t *old = a, *current = b;
+
+ if (old->request == current->request &&
+ old->seqno == current->seqno &&
+ !strcasecmp((const char *)old->domain,
+ (const char *)current->domain)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+vsock_hostlist(const char *vm_name, const char *vm_uuid,
+ int state, void *priv)
+{
+ struct vsock_hostlist_arg *arg = (struct vsock_hostlist_arg *) priv;
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+ uint32_t peer_cid = 0;
+ char peer_cid_str[24];
+
+ ret = get_peer_cid(arg->fd, &peer_cid);
+ if (ret < 0) {
+ printf("Unable to get peer CID: %s\n", strerror(errno));
+ peer_cid_str[0] = '\0';
+ } else
+ snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid);
+
+ /* Noops if auth == AUTH_NONE */
+
+ if (map_check2(arg->map, peer_cid_str, vm_uuid, vm_name) == 0) {
+ /* if we don't have access to fence this VM,
+ * we should not see it in a hostlist either */
+ return 0;
+ }
+
+ strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
+ strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
+ hinfo.state = state;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+vsock_hostlist_begin(int fd)
+{
+ struct timeval tv;
+ char val = (char) RESP_HOSTLIST;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ return _write_retry(fd, &val, 1, &tv);
+}
+
+
+static int
+vsock_hostlist_end(int fd)
+{
+ host_state_t hinfo;
+ struct timeval tv;
+ int ret;
+
+ printf("Sending terminator packet\n");
+
+ memset(&hinfo, 0, sizeof(hinfo));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
+ if (ret == sizeof(hinfo))
+ return 0;
+ return 1;
+}
+
+
+static int
+do_fence_request_vsock(int fd, fence_req_t *req, vsock_info *info)
+{
+ char response = 1;
+ struct vsock_hostlist_arg arg;
+ uint32_t peer_cid = 0;
+ char peer_cid_str[24];
+ int ret;
+
+ ret = get_peer_cid(fd, &peer_cid);
+ if (ret < 0) {
+ printf("Unable to get peer CID: %s\n", strerror(errno));
+ return -1;
+ }
+
+ snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid);
+
+ /* Noops if auth == AUTH_NONE */
+ if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) {
+ printf("CID %u Failed to respond to challenge\n", peer_cid);
+ close(fd);
+ return -1;
+ }
+
+ ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10);
+ if (ret <= 0) {
+ printf("Remote CID %u failed challenge\n", peer_cid);
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(2, "Request %d seqno %d target %s from CID %u\n",
+ req->request, req->seqno, req->domain, peer_cid);
+
+ switch(req->request) {
+ case FENCE_NULL:
+ response = info->cb->null((char *)req->domain, info->priv);
+ break;
+ case FENCE_ON:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->on((char *)req->domain, peer_cid_str,
+ req->seqno, info->priv);
+ break;
+ case FENCE_OFF:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->off((char *)req->domain, peer_cid_str,
+ req->seqno, info->priv);
+ break;
+ case FENCE_REBOOT:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->reboot((char *)req->domain, peer_cid_str,
+ req->seqno, info->priv);
+ break;
+ case FENCE_STATUS:
+ if (map_check(info->map, peer_cid_str,
+ (const char *)req->domain) == 0) {
+ response = RESP_PERM;
+ break;
+ }
+ response = info->cb->status((char *)req->domain, info->priv);
+ break;
+ case FENCE_DEVSTATUS:
+ response = info->cb->devstatus(info->priv);
+ break;
+ case FENCE_HOSTLIST:
+ arg.map = info->map;
+ arg.fd = fd;
+
+ vsock_hostlist_begin(arg.fd);
+ response = info->cb->hostlist(vsock_hostlist, &arg, info->priv);
+ vsock_hostlist_end(arg.fd);
+ break;
+ }
+
+ dbg_printf(3, "Sending response to caller CID %u...\n", peer_cid);
+ if (_write_retry(fd, &response, 1, NULL) < 0)
+ perror("write");
+
+ history_record(info->history, req);
+
+ if (fd != -1)
+ close(fd);
+
+ return 1;
+}
+
+
+static int
+vsock_dispatch(listener_context_t c, struct timeval *timeout)
+{
+ vsock_info *info;
+ fence_req_t data;
+ fd_set rfds;
+ int n;
+ int client_fd;
+ int ret;
+ struct timeval tv;
+
+ if (timeout != NULL)
+ memcpy(&tv, timeout, sizeof(tv));
+ else {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ }
+
+ info = (vsock_info *) c;
+ VALIDATE(info);
+
+ FD_ZERO(&rfds);
+ FD_SET(info->listen_sock, &rfds);
+
+ n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout);
+ if (n <= 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ n = 0;
+ else
+ dbg_printf(2, "select: %s\n", strerror(errno));
+ return n;
+ }
+
+
+ client_fd = accept(info->listen_sock, NULL, NULL);
+ if (client_fd < 0) {
+ perror("accept");
+ return -1;
+ }
+
+ dbg_printf(3, "Accepted vsock client...\n");
+
+ ret = _read_retry(client_fd, &data, sizeof(data), &tv);
+ if (ret != sizeof(data)) {
+ dbg_printf(3, "Invalid request (read %d bytes)\n", ret);
+ close(client_fd);
+ return 0;
+ }
+
+ swab_fence_req_t(&data);
+
+ if (!verify_request(&data, info->args.hash, info->key, info->key_len)) {
+ printf("Key mismatch; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ dbg_printf(3, "Request %d seqno %d domain %s\n",
+ data.request, data.seqno, data.domain);
+
+ if (history_check(info->history, &data) == 1) {
+ printf("We just did this request; dropping client\n");
+ close(client_fd);
+ return 0;
+ }
+
+ switch(info->args.auth) {
+ case AUTH_NONE:
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ printf("VSOCK request\n");
+ do_fence_request_vsock(client_fd, &data, info);
+ break;
+ default:
+ printf("XXX Unhandled authentication\n");
+ }
+
+ return 0;
+}
+
+
+static int
+vsock_config(config_object_t *config, vsock_options *args)
+{
+ char value[1024];
+ int errors = 0;
+
+ if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
+ dset(atoi(value));
+
+ if (sc_get(config, "listeners/vsock/@key_file",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for key_file\n", value);
+ args->key_file = strdup(value);
+ } else {
+ args->key_file = strdup(DEFAULT_KEY_FILE);
+ if (!args->key_file) {
+ dbg_printf(1, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ args->hash = DEFAULT_HASH;
+ if (sc_get(config, "listeners/vsock/@hash",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for hash\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = HASH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = HASH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = HASH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = HASH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported hash: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->auth = DEFAULT_AUTH;
+ if (sc_get(config, "listeners/vsock/@auth",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for auth\n", value);
+ if (!strcasecmp(value, "none")) {
+ args->hash = AUTH_NONE;
+ } else if (!strcasecmp(value, "sha1")) {
+ args->hash = AUTH_SHA1;
+ } else if (!strcasecmp(value, "sha256")) {
+ args->hash = AUTH_SHA256;
+ } else if (!strcasecmp(value, "sha512")) {
+ args->hash = AUTH_SHA512;
+ } else {
+ dbg_printf(1, "Unsupported auth: %s\n", value);
+ ++errors;
+ }
+ }
+
+ args->port = DEFAULT_MCAST_PORT;
+ if (sc_get(config, "listeners/vsock/@port",
+ value, sizeof(value)-1) == 0) {
+ dbg_printf(1, "Got %s for port\n", value);
+ args->port = atoi(value);
+ if (args->port <= 0) {
+ dbg_printf(1, "Invalid port: %s\n", value);
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+
+static int
+vsock_init(listener_context_t *c, const fence_callbacks_t *cb,
+ config_object_t *config, map_object_t *map, void *priv)
+{
+ vsock_info *info;
+ int listen_sock, ret;
+ struct sockaddr_vm svm;
+
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ printf("Could not initialize NSS\n");
+ return 1;
+ }
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return -1;
+
+ info->priv = priv;
+ info->cb = cb;
+ info->map = map;
+
+ ret = vsock_config(config, &info->args);
+ if (ret < 0)
+ perror("vsock_config");
+ else if (ret > 0)
+ printf("%d errors found during vsock listener configuration\n", ret);
+
+ if (ret != 0) {
+ if (info->args.key_file)
+ free(info->args.key_file);
+ free(info);
+ return -1;
+ }
+
+ if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
+ info->key_len = read_key_file(info->args.key_file,
+ info->key, sizeof(info->key));
+ if (info->key_len < 0) {
+ printf("Could not read %s; operating without "
+ "authentication\n", info->args.key_file);
+ info->args.auth = AUTH_NONE;
+ info->args.hash = HASH_NONE;
+ info->key_len = 0;
+ }
+ }
+
+ listen_sock = socket(PF_VSOCK, SOCK_STREAM, 0);
+ if (listen_sock < 0)
+ goto out_fail;
+
+ memset(&svm, 0, sizeof(svm));
+ svm.svm_family = AF_VSOCK;
+ svm.svm_cid = VMADDR_CID_ANY;
+ svm.svm_port = info->args.port;
+
+ if (bind(listen_sock, (struct sockaddr *) &svm, sizeof(svm)) < 0)
+ goto out_fail;
+
+ if (listen(listen_sock, 1) < 0)
+ goto out_fail;
+
+ info->magic = VSOCK_MAGIC;
+ info->listen_sock = listen_sock;
+ info->history = history_init(check_history, 10, sizeof(fence_req_t));
+ *c = (listener_context_t)info;
+ return 0;
+
+out_fail:
+ printf("Could not set up listen socket: %s\n", strerror(errno));
+ if (listen_sock >= 0)
+ close(listen_sock);
+ if (info->args.key_file)
+ free(info->args.key_file);
+ free(info);
+ return -1;
+}
+
+
+static int
+vsock_shutdown(listener_context_t c)
+{
+ vsock_info *info = (vsock_info *)c;
+
+ VALIDATE(info);
+ info->magic = 0;
+ history_wipe(info->history);
+ free(info->history);
+ free(info->args.key_file);
+ close(info->listen_sock);
+ free(info);
+
+ return 0;
+}
+
+
+static listener_plugin_t vsock_plugin = {
+ .name = NAME,
+ .version = VSOCK_VERSION,
+ .init = vsock_init,
+ .dispatch = vsock_dispatch,
+ .cleanup = vsock_shutdown,
+};
+
+double
+LISTENER_VER_SYM(void)
+{
+ return PLUGIN_VERSION_LISTENER;
+}
+
+const listener_plugin_t *
+LISTENER_INFO_SYM(void)
+{
+ return &vsock_plugin;
+}