diff --git a/agents/virt/include/server_plugin.h b/agents/virt/include/server_plugin.h index abfbdbe9..1f8e931c 100644 --- a/agents/virt/include/server_plugin.h +++ b/agents/virt/include/server_plugin.h @@ -1,133 +1,131 @@ /* */ #ifndef _SERVER_PLUGIN_H #define _SERVER_PLUGIN_H #include "config.h" #define PLUGIN_VERSION_LISTENER ((double)0.3) #define PLUGIN_VERSION_BACKEND ((double)0.2) typedef void * listener_context_t; typedef void * backend_context_t; /* These callbacks hand requests off to the appropriate backend. */ /* Do nothing. Returns 1 (failure) to caller */ typedef int (*fence_null_callback)(const char *vm_name, void *priv); /* Turn the VM 'off'. Returns 0 to caller if successful or nonzero if unsuccessful. */ typedef int (*fence_off_callback)(const char *vm_name, const char *src, uint32_t seqno, void *priv); /* Turn the VM 'on'. Returns 0 to caller if successful or nonzero if unsuccessful. */ typedef int (*fence_on_callback)(const char *vm_name, const char *src, uint32_t seqno, void *priv); /* Reboot a VM. Returns 0 to caller if successful or nonzero if unsuccessful. */ typedef int (*fence_reboot_callback)(const char *vm_name, const char *src, uint32_t seqno, void *priv); /* Get status of a VM. Returns 0 to caller if VM is alive or nonzero if VM is not alive. */ typedef int (*fence_status_callback)(const char *vm_name, void *priv); /* Get status of backend. Returns 0 to caller if backend is responding to requests. */ typedef int (*fence_devstatus_callback)(void *priv); /* VMs available to fence. Returns 0 to caller if backend is responding to requests and a host list can be produced */ typedef int (*hostlist_callback)(const char *vm_name, const char *uuid, int state, void *arg); typedef int (*fence_hostlist_callback)(hostlist_callback cb, void *arg, void *priv); typedef int (*backend_init_fn)(backend_context_t *c, config_object_t *config); typedef int (*backend_cleanup_fn)(backend_context_t c); typedef struct _fence_callbacks { fence_null_callback null; fence_off_callback off; fence_on_callback on; fence_reboot_callback reboot; fence_status_callback status; fence_devstatus_callback devstatus; fence_hostlist_callback hostlist; } fence_callbacks_t; typedef struct backend_plugin { const char *name; const char *version; const fence_callbacks_t *callbacks; backend_init_fn init; backend_cleanup_fn cleanup; } backend_plugin_t; double backend_plugin_version(void); const backend_plugin_t * backend_plugin_info(void); #define BACKEND_VER_SYM backend_plugin_version #define BACKEND_INFO_SYM backend_plugin_info #define BACKEND_VER_STR "backend_plugin_version" #define BACKEND_INFO_STR "backend_plugin_info" typedef int (*listener_init_fn)(listener_context_t *c, const fence_callbacks_t *cb, config_object_t *config, map_object_t *map, void *priv); typedef int (*listener_dispatch_fn)(listener_context_t c, struct timeval *timeout); typedef int (*listener_cleanup_fn)(listener_context_t c); typedef struct listener_plugin { const char *name; const char *version; listener_init_fn init; listener_dispatch_fn dispatch; listener_cleanup_fn cleanup; } listener_plugin_t; double listener_plugin_version(void); const listener_plugin_t * listener_plugin_info(void); #define LISTENER_VER_SYM listener_plugin_version #define LISTENER_INFO_SYM listener_plugin_info #define LISTENER_VER_STR "listener_plugin_version" #define LISTENER_INFO_STR "listener_plugin_info" typedef enum { PLUGIN_NONE = 0, PLUGIN_LISTENER = 1, PLUGIN_BACKEND = 2 } plugin_type_t; #ifdef __cplusplus extern "C" { #endif int plugin_reg_backend(const backend_plugin_t *plugin); int plugin_reg_listener(const listener_plugin_t *plugin); const backend_plugin_t *plugin_find_backend(const char *name); const listener_plugin_t *plugin_find_listener(const char *name); void plugin_dump(void); -#ifdef _MODULE int plugin_load(const char *filename); int plugin_search(const char *pathname); -#endif #ifdef __cplusplus } #endif #endif diff --git a/agents/virt/server/Makefile.am b/agents/virt/server/Makefile.am index 787e3a85..9c839a36 100644 --- a/agents/virt/server/Makefile.am +++ b/agents/virt/server/Makefile.am @@ -1,172 +1,104 @@ ############################################################################### ############################################################################### ## ## 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) -# -# modules -# - virt_la_SOURCES = libvirt.c null_la_SOURCES = null.c virt_qmf_la_SOURCES = libvirt-qmf.cpp pm_fence_la_SOURCES = pm-fence.c cpg_la_SOURCES = cpg-virt.c cpg.c multicast_la_SOURCES = mcast.c tcp_la_SOURCES = tcp.c vsock_la_SOURCES = vsock.c serial_la_SOURCES = virt-serial.c virt-sockets.c serial.c -################# -# Modular build # -################# -if modularbuild - 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_SOURCES += virt.c uuid-test.c virt_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(virt_CFLAGS) virt_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(virt_LIBS) endif if modlibvirtqmf fvlib_LTLIBRARIES += virt-qmf.la virt_qmf_la_SOURCES += uuid-test.c virt_qmf_la_CXXFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(qpid_CFLAGS) virt_qmf_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(qpid_LIBS) endif if modpmfence fvlib_LTLIBRARIES += pm-fence.la pm_fence_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(cib_CFLAGS) $(glib2_CFLAGS) $(xml2_CFLAGS) pm_fence_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(cib_LIBS) $(glib2_LIBS) $(xml2_LIBS) endif if modcpg fvlib_LTLIBRARIES += cpg.la cpg_la_SOURCES += virt.c history.c uuid-test.c 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_SOURCES += history.c multicast_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) multicast_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) endif if modserial fvlib_LTLIBRARIES += serial.la serial_la_SOURCES += history.c 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_SOURCES += history.c tcp_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) tcp_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) endif if modvsock fvlib_LTLIBRARIES += vsock.la vsock_la_SOURCES += history.c vsock_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) vsock_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) endif if modnull fvlib_LTLIBRARIES += null.la null_la_CFLAGS = $(MODULESCFLAGS) null_la_LDFLAGS = $(MODULESLDFLAGS) endif - -else - -#################### -# Monolithic build # -#################### - -# avoid a linking issues when: -# 1) do a server modular build -# 2) run configure to switch to monolithic build without make clean -# 3) do a server build -# -# libtool will get confused by the presence of .la files in the builddir -# and will fail to link the binary - -EXTRA_fence_virtd_DEPENDENCIES = cleanmods - -cleanmods: - @rm -rf $(builddir)/*.la - -fence_virtd_SOURCES += virt.c history.c - -if modlibvirt -fence_virtd_SOURCES += $(libvirt_la_SOURCES) -fence_virtd_CFLAGS += $(virt_CFLAGS) -fence_virtd_LDADD += $(virt_LIBS) -endif -if modlibvirtqmf -fence_virtd_SOURCES += $(libvirt_qmf_la_SOURCES) -fence_virtd_CFLAGS += $(qpid_CFLAGS) -fence_virtd_LDADD += $(qpid_LIBS) -endif -if modpmfence -fence_virtd_SOURCES += $(pm_fence_la_SOURCES) -fence_virtd_CFLAGS += $(cib_CFLAGS) -fence_virtd_LDADD += $(cib_LIBS) -endif -if modcpg -fence_virtd_SOURCES += $(cpg_la_SOURCES) -fence_virtd_CFLAGS += $(cpg_CFLAGS) -fence_virtd_LDADD += $(cpg_LIBS) -endif -if modmulticast -fence_virtd_SOURCES += $(multicast_la_SOURCES) -endif -if modserial -fence_virtd_SOURCES += $(serial_la_SOURCES) -endif -if modtcp -fence_virtd_SOURCES += $(tcp_la_SOURCES) -endif -if modvsock -fence_virtd_SOURCES += $(vsock_la_SOURCES) -endif -if modnull -fence_virtd_SOURCES += $(null_la_SOURCES) -endif - -endif diff --git a/agents/virt/server/config.c b/agents/virt/server/config.c index 2d41100c..6b7e3ec8 100644 --- a/agents/virt/server/config.c +++ b/agents/virt/server/config.c @@ -1,663 +1,661 @@ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #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, char *dfl, char *input, size_t len) { printf("%s [%s]: ", prompt, dfl?dfl:""); fflush(stdout); memset(input, 0, len); if (fgets(input, len, stdin) == NULL) { strncpy(input, dfl, len); return 0; } if (input[strlen(input)-1] == '\n') input[strlen(input)-1] = 0; if (strlen(input) == 0) { strncpy(input, dfl, len); return 0; } return 0; } static int plugin_path_configure(config_object_t *config) { -#ifdef _MODULE 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); -#endif 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 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 index 3a9c2dd2..304519c4 100644 --- a/agents/virt/server/cpg-virt.c +++ b/agents/virt/server/cpg-virt.c @@ -1,654 +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 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #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; -#ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0) dset(atoi(value)); -#endif 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, }; - -#ifdef _MODULE double BACKEND_VER_SYM(void) { return PLUGIN_VERSION_BACKEND; } const backend_plugin_t * BACKEND_INFO_SYM(void) { return &cpg_virt_plugin; } -#else -static void __attribute__((constructor)) -cpg_register_plugin(void) -{ - plugin_reg_backend(&cpg_virt_plugin); -} -#endif diff --git a/agents/virt/server/libvirt.c b/agents/virt/server/libvirt.c index 8467590d..8f010452 100644 --- a/agents/virt/server/libvirt.c +++ b/agents/virt/server/libvirt.c @@ -1,370 +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 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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); -#ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0) dset(atoi(value)); -#endif 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, }; - -#ifdef _MODULE double BACKEND_VER_SYM(void) { return PLUGIN_VERSION_BACKEND; } const backend_plugin_t * BACKEND_INFO_SYM(void) { return &libvirt_plugin; } -#else -static void __attribute__((constructor)) -libvirt_register_plugin(void) -{ - plugin_reg_backend(&libvirt_plugin); -} -#endif diff --git a/agents/virt/server/main.c b/agents/virt/server/main.c index 9da8f0ea..8d9c3c19 100644 --- a/agents/virt/server/main.c +++ b/agents/virt/server/main.c @@ -1,266 +1,264 @@ #include "config.h" #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.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 Use as configuration file.\n"); printf(" -d Set debugging level to .\n"); printf(" -c Configuration mode.\n"); printf(" -l List plugins.\n"); printf(" -w Wait for initialization.\n"); printf(" -p Use 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); -#ifdef _MODULE 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; } -#endif 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])); } 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); daemon_cleanup(); return 0; } diff --git a/agents/virt/server/mcast.c b/agents/virt/server/mcast.c index bf107237..a76888f9 100644 --- a/agents/virt/server/mcast.c +++ b/agents/virt/server/mcast.c @@ -1,634 +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 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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.2" #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_check(arg->map, arg->src, vm_uuid) == 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; -#ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); -#endif 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, }; - -#ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &mcast_plugin; } -#else -static void __attribute__((constructor)) -mcast_register_plugin(void) -{ - plugin_reg_listener(&mcast_plugin); -} -#endif - diff --git a/agents/virt/server/serial.c b/agents/virt/server/serial.c index f218bea3..0aa73859 100644 --- a/agents/virt/server/serial.c +++ b/agents/virt/server/serial.c @@ -1,471 +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 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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.4" #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_check(arg->map, arg->src, vm_uuid) == 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; -#ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); -#endif 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 uri\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, }; - -#ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &serial_plugin; } -#else -static void __attribute__((constructor)) -serial_register_plugin(void) -{ - plugin_reg_listener(&serial_plugin); -} -#endif - diff --git a/agents/virt/server/tcp.c b/agents/virt/server/tcp.c index 1ffb5a56..d39f3579 100644 --- a/agents/virt/server/tcp.c +++ b/agents/virt/server/tcp.c @@ -1,563 +1,552 @@ /* 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 #include #include #include #include #include #include #include /* 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.1" #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_check(arg->map, arg->src, vm_uuid) == 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 int do_fence_request_tcp(int fd, 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; } dbg_printf(2, "Request %d seqno %d target %s\n", req->request, req->seqno, 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; 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, NULL, NULL); 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, &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; -#ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); -#endif 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, }; - -#ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &tcp_plugin; } -#else -static void __attribute__((constructor)) -tcp_register_plugin(void) -{ - plugin_reg_listener(&tcp_plugin); -} -#endif diff --git a/agents/virt/server/vsock.c b/agents/virt/server/vsock.c index 93f5880f..3ea26fb9 100644 --- a/agents/virt/server/vsock.c +++ b/agents/virt/server/vsock.c @@ -1,576 +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 #include #include #include #include #include #include #include #include #include /* 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.1" #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_check(arg->map, peer_cid_str, vm_uuid) == 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; -#ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); -#endif 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, }; - -#ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &vsock_plugin; } -#else -static void __attribute__((constructor)) -vsock_register_plugin(void) -{ - plugin_reg_listener(&vsock_plugin); -} -#endif diff --git a/configure.ac b/configure.ac index 82c553cb..8faf820d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,623 +1,609 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) # TODO: port .gitarchiver AC_INIT([fence-agents], [m4_esyscmd([make/git-version-gen .tarball-version])], [developers@clusterlabs.org]) AC_CONFIG_AUX_DIR([.]) # Don't let AC_PROC_CC (invoked by AC_USE_SYSTEM_EXTENSIONS) replace # undefined CFLAGS with -g -O2, overriding our special OPT_CFLAGS. : ${CFLAGS=""} AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([1.13 dist-bzip2 dist-xz color-tests -Wno-portability subdir-objects]) # Sanitize path if test "$prefix" = "NONE"; then prefix="/usr" if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi case $exec_prefix in NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac # It is necessary to have this done before libtool does linker detection. # See also: https://github.com/kronosnet/kronosnet/issues/107 # --as-needed: Modern systems have builtin ceil() making -lm superfluous but # AC_SEARCH_LIBS can't detect this because it tests with a false prototype AX_CHECK_LINK_FLAG([-Wl,--enable-new-dtags], [AM_LDFLAGS=-Wl,--enable-new-dtags], [AC_MSG_ERROR(["Linker support for --enable-new-dtags is required"])]) AX_CHECK_LINK_FLAG([-Wl,--as-needed], [AM_LDFLAGS="$AM_LDFLAGS -Wl,--as-needed"]) LT_PREREQ([2.2.6]) LT_INIT AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([lib/fencing.py.py]) AC_CONFIG_HEADERS([make/config.h]) AC_CANONICAL_HOST AC_PROG_LIBTOOL AC_LANG([C]) # Checks for programs. # check stolen from gnulib/m4/gnu-make.m4 if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then AC_MSG_ERROR([you don't seem to have GNU make; it is required]) fi AC_PROG_CC AM_PROG_CC_C_O AC_PROG_GCC_TRADITIONAL AC_PROG_LN_S AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_AWK AC_PROG_CXX AC_PROG_YACC AC_PROG_LEX PKG_PROG_PKG_CONFIG ## local helper functions # this function checks if CC support options passed as # args. Global CFLAGS are ignored during this test. cc_supports_flag() { local CFLAGS="-Werror $@" AC_MSG_CHECKING([whether $CC supports "$@"]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ ]])], [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) return $RC } # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_HEADER_TIME AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h syslog.h termios.h unistd.h libintl.h limits.h netdb.h stddef.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_C_CONST AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT32_T AC_TYPE_OFF_T AC_TYPE_SIGNAL # Checks for library functions. AC_FUNC_FORK AC_FUNC_MALLOC AC_FUNC_CLOSEDIR_VOID AC_FUNC_MEMCMP AC_FUNC_SELECT_ARGTYPES AC_FUNC_STAT AC_CHECK_FUNCS([alarm atexit bzero dup2 memmove memset select socket strcasecmp strchr strdup strerror strtol gettimeofday]) # local options AC_ARG_ENABLE([debug], [ --enable-debug enable debug build. ], [ default="no" ]) AC_ARG_WITH([fenceagentslibdir], [ --with-fenceagentslibdir=PATH installation path for fence library. ], [ FENCEAGENTSLIBDIR="$withval" ], [ FENCEAGENTSLIBDIR="${datadir}/fence" ]) AC_ARG_WITH([agents], [ --with-agents=LIST list of agents to build/ship (default: all). ], [ AGENTS_LIST="$withval" ], [ AGENTS_LIST="all" ]) if test "x$AGENTS_LIST" = x; then AC_ERROR([No agents selected]) fi FENCE_KDUMP=0 if echo "$AGENTS_LIST" | grep -q -E "all|kdump"; then case "$host_os" in *bsd*) ;; *) FENCE_KDUMP=1 ;; esac AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s/kdump( |$)//") fi FENCE_MANUAL=0 if echo "$AGENTS_LIST" | grep -q -E "all|manual"; then FENCE_MANUAL=1 AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s/manual( |$)//") fi FENCE_MPATH=0 if echo "$AGENTS_LIST" | grep -q -E "all|mpath"; then FENCE_MPATH=1 fi FENCE_SCSI=0 if echo "$AGENTS_LIST" | grep -q -E "all|scsi"; then FENCE_SCSI=1 fi FENCE_ZVM=0 if echo "$AGENTS_LIST" | grep -q -E "all|zvm( |$)"; then FENCE_ZVM=1 fi FENCE_VIRT=0 if echo "$AGENTS_LIST" | grep -q -E "all|virt( |$)"; then case "$host_os" in *bsd*) ;; *) FENCE_VIRT=1 VIRT_AM_LDFLAGS="$AM_LDFLAGS -fPIC -fPIE -Wl,-z,now" AC_SUBST([VIRT_AM_LDFLAGS]) VIRT_AM_CFLAGS="-fPIC -fPIE -I\$(top_srcdir)/agents/virt/include -D_GNU_SOURCE" AC_SUBST([VIRT_AM_CFLAGS]) VIRT_COMMON_LDFLAGS="-Wl,-wrap,syslog,-wrap,closelog" AC_SUBST([VIRT_COMMON_LDFLAGS]) VIRT_COMMON_LIBS="-Wl,-Bstatic -L\$(top_builddir)/agents/virt/common -lfence_virt -Wl,-Bdynamic" AC_SUBST([VIRT_COMMON_LIBS]) VIRT_CONFIG_LIBS="-L\$(top_builddir)/agents/virt/config -lsimpleconfig" AC_SUBST([VIRT_CONFIG_LIBS]) # Checks for libraries. AX_PTHREAD(,[AC_MSG_ERROR([POSIX threads support is required])]) PKG_CHECK_MODULES([nss], [nss]) PKG_CHECK_MODULES([xml2], [libxml-2.0]) PKG_CHECK_MODULES([uuid], [uuid]) saved_LIBS="$LIBS" LIBS= AC_SEARCH_LIBS([dlopen], [dl dld], , [AC_MSG_ERROR([dlopen not found])]) AC_SUBST([dl_LIBS], [$LIBS]) LIBS="$saved_LIBS" ;; esac fi if test "x$AGENTS_LIST" != xall; then for j in $AGENTS_LIST; do if ! test -f agents/$j/fence_$j*.py; then AC_ERROR([Agent $j does not exists]) fi AGENTS_LIST=`echo "$AGENTS_LIST" | sed -E -e "s#$j([^_/]|$)#$j/fence_$j\1#g" -e "s#zvm/fence_zvm( |$)#zvm/fence_zvmip\1#g"` done fi if test "x$AGENTS_LIST" = xall; then AGENTS_LIST=`find $srcdir/agents -mindepth 2 -maxdepth 2 -name 'fence_*.py' -print0 | xargs -0 | sed -E -e 's#[^ ]*/agents/##g' -e 's#lib/[A-Za-z_.]*( |$)##g' -e 's#nss_wrapper/[A-Za-z_.]*( |$)##g' -e 's#autodetect/[A-Za-z_.]*( |$)##g'` fi XENAPILIB=0 if echo "$AGENTS_LIST" | grep -q xenapi; then XENAPILIB=1 fi ## random vars LOGDIR=${localstatedir}/log/cluster CLUSTERVARRUN=${localstatedir}/run/cluster CLUSTERDATA=${datadir}/cluster AC_PATH_PROGS(XMLLINT, xmllint) AM_CONDITIONAL(BUILD_DOC, test "x$XMLLINT" != "x" ) if test "x$XMLLINT" = "x"; then AC_MSG_WARN([xmllint not installed, unable to (re-)build manual pages]) exit 1 fi AC_SUBST(XMLLINT) AC_PATH_PROGS(XSLTPROC, xsltproc) AM_CONDITIONAL(BUILD_DOC, test "x$XSLTPROC" != "x" ) if test "x$XSLTPROC" = "x"; then AC_MSG_WARN([xsltproc not installed, unable to (re-)build manual pages]) exit 1 fi AC_SUBST(XSLTPROC) AM_PATH_PYTHON if test -z "$PYTHON"; then echo "*** Essential program python not found" 1>&2 exit 1 fi dnl Ensure PYTHON is an absolute path AC_PATH_PROG([PYTHON], [$PYTHON]) AC_PYTHON_MODULE(pexpect, 1) AC_PYTHON_MODULE(pycurl, 1) AC_PYTHON_MODULE(requests, 1) if echo "$AGENTS_LIST" | grep -q amt_ws; then AC_PYTHON_MODULE(pywsman) if test "x${HAVE_PYMOD_PYWSMAN}" != xyes; then AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#amt_ws/fence_amt_ws.py( |$)##") AC_MSG_WARN("Not building fence_amt_ws") fi fi if echo "$AGENTS_LIST" | grep -q aws; then AC_PYTHON_MODULE(boto3) if test "x${HAVE_PYMOD_BOTO3}" != xyes; then AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#aws/fence_aws.py( |$)##") AC_MSG_WARN("Not building fence_aws") fi fi if echo "$AGENTS_LIST" | grep -q -E "ovh|vmware_soap"; then AC_PYTHON_MODULE(suds) if test "x${HAVE_PYMOD_SUDS}" != xyes; then AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#(ovh/fence_ovh|vmware_soap/fence_vmware_soap).py( |$)##g") AC_MSG_WARN("Not building fence_ovh and fence_vmware_soap") fi fi if echo "$AGENTS_LIST" | grep -q gce; then AC_PYTHON_MODULE(googleapiclient) AC_PYTHON_MODULE(oauth2client) if test "x${HAVE_PYMOD_GOOGLEAPICLIENT}" != xyes; then AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#gce/fence_gce.py( |$)##") AC_MSG_WARN("Not building fence_ovh and fence_gce") fi fi ## path to 3rd-party binaries AC_PATH_PROG([IPMITOOL_PATH], [ipmitool], [/usr/bin/ipmitool]) AC_PATH_PROG([OPENSTACK_PATH], [openstack], [/usr/bin/openstack]) AC_PATH_PROG([AMTTOOL_PATH], [amttool], [/usr/bin/amttool]) AC_PATH_PROG([GNUTLSCLI_PATH], [gnutlscli], [/usr/bin/gnutls-cli]) AC_PATH_PROG([COROSYNC_CMAPCTL_PATH], [corosync-cmapctl], [/usr/sbin/corosync-cmapctl]) AC_PATH_PROG([SG_PERSIST_PATH], [sg_persist], [/usr/bin/sg_persist]) AC_PATH_PROG([SG_TURS_PATH], [sg_turs], [/usr/bin/sg_turs]) AC_PATH_PROG([VGS_PATH], [vgs], [/usr/sbin/vgs]) AC_PATH_PROG([SUDO_PATH], [sudo], [/usr/bin/sudo]) AC_PATH_PROG([SSH_PATH], [ssh], [/usr/bin/ssh]) AC_PATH_PROG([TELNET_PATH], [telnet], [/usr/bin/telnet]) AC_PATH_PROG([MPATH_PATH], [mpathpersist], [/usr/sbin/mpathpersist]) AC_PATH_PROG([SBD_PATH], [sbd], [/sbin/sbd]) AC_PATH_PROG([SUDO_PATH], [sudo], [/usr/bin/sudo]) AC_PATH_PROG([SNMPWALK_PATH], [snmpwalk], [/usr/bin/snmpwalk]) AC_PATH_PROG([SNMPSET_PATH], [snmpset], [/usr/bin/snmpset]) AC_PATH_PROG([SNMPGET_PATH], [snmpget], [/usr/bin/snmpget]) AC_PATH_PROG([NOVA_PATH], [nova], [/usr/bin/nova]) AC_PATH_PROG([POWERMAN_PATH], [powerman], [/usr/bin/powerman]) AC_PATH_PROG([PING_CMD], [ping]) AC_PATH_PROG([PING6_CMD], [ping6]) AC_PATH_PROG([PING4_CMD], [ping4]) if test "x${ac_cv_path_PING_CMD}" = x; then # assume multicall-ping just not available in build-environment PING_CMD="/bin/ping" PING4_CMD="/bin/ping -4" PING6_CMD="/bin/ping -6" elif test "x${ac_cv_path_PING6_CMD}" = x; then # just IPv4 PING4_CMD="${ac_cv_path_PING_CMD}" elif test -L ${ac_cv_path_PING6_CMD}; then # assume multicall-ping PING4_CMD="${ac_cv_path_PING_CMD} -4" else # ping is just IPv4 PING4_CMD="${ac_cv_path_PING_CMD}" fi ## do subst AC_SUBST([LOGDIR]) AC_SUBST([CLUSTERVARRUN]) AC_SUBST([CLUSTERDATA]) AC_SUBST([FENCEAGENTSLIBDIR]) AC_SUBST([SNMPBIN]) AC_SUBST([AGENTS_LIST]) AM_CONDITIONAL(BUILD_FENCE_KDUMP, test $FENCE_KDUMP -eq 1) AM_CONDITIONAL(BUILD_FENCE_MANUAL, test $FENCE_MANUAL -eq 1) AM_CONDITIONAL(BUILD_FENCE_MPATH, test $FENCE_MPATH -eq 1) AM_CONDITIONAL(BUILD_FENCE_SCSI, test $FENCE_SCSI -eq 1) AM_CONDITIONAL(BUILD_FENCE_ZVM, test $FENCE_ZVM -eq 1) AM_CONDITIONAL(BUILD_FENCE_VIRT, test $FENCE_VIRT -eq 1) AM_CONDITIONAL(BUILD_XENAPILIB, test $XENAPILIB -eq 1) AC_SUBST([IPMITOOL_PATH]) AC_SUBST([OPENSTACK_PATH]) AC_SUBST([AMTTOOL_PATH]) AC_SUBST([COROSYNC_CMAPCTL_PATH]) AC_SUBST([SG_PERSIST_PATH]) AC_SUBST([SG_TURS_PATH]) AC_SUBST([VGS_PATH]) AC_SUBST([POWERMAN_PATH]) ## fence-virt stuff if test "x$FENCE_VIRT" = "x1"; then sysconf=$(eval echo $sysconfdir) AC_DEFINE_UNQUOTED([SYSCONFDIR], ["$sysconf"], [Default config dir]) fi -# -# Modular build for fence_virtd to split up dependencies -# (default) -# -AC_ARG_ENABLE(modules, -[AS_HELP_STRING([--disable-modules], - [Disable modular build])], -[ modules=$enableval ], [ modules=yes ]) -AM_CONDITIONAL([modularbuild], [test "x$modules" == "xyes"]) - -if test "x$modules" == "xyes"; then - AC_DEFINE_UNQUOTED([_MODULE], [1], [modular build]) -fi - ### The following options only are used when $modules="yes" ### # Null plugin: Disabled by default AC_ARG_ENABLE(null-plugin, [AS_HELP_STRING([--enable-null-plugin], [Enable null (no-op) backend plugin])], [ modnull=$enableval ], [ modnull=no ]) AM_CONDITIONAL([modnull], [test "x$modnull" == "xyes"]) # libvirt plugin: Enabled by default AC_ARG_ENABLE(libvirt-plugin, [AS_HELP_STRING([--disable-libvirt-plugin], [Disable local-mode libvirt backend plugin])], [ modlibvirt=$enableval ], [ modlibvirt=yes ]) AM_CONDITIONAL([modlibvirt], [test "x$modlibvirt" == "xyes"]) if test "x$modlibvirt" == "xyes" && test "x$FENCE_VIRT" = "x1"; then PKG_CHECK_MODULES([virt], [libvirt]) fi # cpg plugin: Disabled by default AC_ARG_ENABLE(cpg-plugin, [AS_HELP_STRING([--enable-cpg-plugin], [Enable CPG/libvirt backend plugin])], [ modcpg=$enableval ], [ modcpg=no ]) AM_CONDITIONAL([modcpg], [test "x$modcpg" == "xyes"]) if test "x$modcpg" == "xyes" && test "x$FENCE_VIRT" = "x1"; then PKG_CHECK_MODULES([cpg], [libcpg]) fi # (broken!) libvirt-qmf plugin: Disabled by default AC_ARG_ENABLE(libvirt-qmf-plugin, [AS_HELP_STRING([--enable-libvirt-qmf-plugin], [Enable libvirt-qmf backend plugin])], [ modlibvirtqmf=$enableval ], [ modlibvirtqmf=no ]) AM_CONDITIONAL([modlibvirtqmf], [test "x$modlibvirtqmf" == "xyes"]) if test "x$modlibvirtqmf" == "xyes" && test "x$FENCE_VIRT" = "x1"; then PKG_CHECK_MODULES([qpid], [qpid]) fi # (broken with pcmk 2.0!) pm-fence plugin: Disabled by default AC_ARG_ENABLE(pm-fence-plugin, [AS_HELP_STRING([--enable-pm-fence-plugin], [Enable pm-fence backend plugin])], [ modpmfence=$enableval ], [ modpmfence=no ]) AM_CONDITIONAL([modpmfence], [test "x$modpmfence" == "xyes"]) if test "x$modpmfence" == "xyes" && test "x$FENCE_VIRT" = "x1"; then PKG_CHECK_MODULES([cib], [pacemaker-cib]) PKG_CHECK_MODULES([ncurses], [ncurses]) PKG_CHECK_MODULES([glib2], [glib-2.0]) AC_SEARCH_LIBS([read_attr_delegate], [cib], [ ], [ AC_DEFINE_UNQUOTED([PM_1_0], [1], [pacemaker 1.0]) ]) fi # multicast plugin: Enabled by default AC_ARG_ENABLE(multicast-plugin, [AS_HELP_STRING([--disable-multicast-plugin], [Disable multicast listener plugin])], [ modmulticast=$enableval ], [ modmulticast=yes ]) AM_CONDITIONAL([modmulticast], [test "x$modmulticast" == "xyes"]) # tcp plugin: Enabled by default AC_ARG_ENABLE(tcp-plugin, [AS_HELP_STRING([--disable-tcp-plugin], [Disable TCP listener plugin])], [ modtcp=$enableval ], [ modtcp=yes ]) AM_CONDITIONAL([modtcp], [test "x$modtcp" == "xyes"]) # serial/libvirt plugin: Enabled by default AC_ARG_ENABLE(serial-plugin, [AS_HELP_STRING([--disable-serial-plugin], [Disable serial listener plugin])], [ modserial=$enableval ], [ modserial=yes ]) AM_CONDITIONAL([modserial], [test "x$modserial" == "xyes"]) # vsock plugin: Enabled by default AC_ARG_ENABLE(vsock-plugin, [AS_HELP_STRING([--disable-vsock-plugin], [Disable TCP listener plugin])], [ modvsock=$enableval ], [ modvsock=yes ]) AM_CONDITIONAL([modvsock], [test "x$modvsock" == "xyes"]) # # Compatibility symlink: enabled by default # AC_ARG_ENABLE(xvm-compat, [AS_HELP_STRING([--disable-xvm-compat], [Disable fence_xvm symlink compatibility])], [ xvmcompat=$enableval ], [ xvmcompat=yes ]) AM_CONDITIONAL([xvmcompat], [test "x$xvmcompat" == "xyes"]) # Try to detect the appropriate conf dir. Several systems have both /etc/default # and /etc/sysconfig but latter is always primary. AC_ARG_VAR(initconfdir, [directory for initscripts configuration]) if test "x$initconfdir" = x && test "x$FENCE_VIRT" = "x1"; then AC_CHECK_FILE(/etc/conf.d, [initconfdir='$(sysconfdir)/conf.d}'], [# Gentoo/Arch AC_CHECK_FILE(/etc/sysconfig, [initconfdir='$(sysconfdir)/sysconfig'], [# RedHat/Fedora/Slax/Mandriva/S AC_CHECK_FILE(/etc/default, [initconfdir='$(sysconfdir)/default'], [# Debian/Ubuntu AC_MSG_ERROR([could not determine system initscripts config dir; please set initconfdir manually.])])])]) fi ## *FLAGS handling ENV_CFLAGS="$CFLAGS" ENV_CPPFLAGS="$CPPFLAGS" ENV_LDFLAGS="$LDFLAGS" # debug build stuff if test "x${enable_debug}" = xyes; then AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code]) OPT_CFLAGS="-O0" else OPT_CFLAGS="-O2" fi # gdb flags if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi # extra warnings EXTRA_WARNINGS="" WARNLIST=" error all shadow missing-prototypes missing-declarations strict-prototypes declaration-after-statement pointer-arith write-strings cast-align bad-function-cast missing-format-attribute format=2 format-security format-nonliteral no-long-long unsigned-char gnu89-inline no-strict-aliasing " for j in $WARNLIST; do if cc_supports_flag -W$j; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j"; fi done AM_CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS $EXTRA_WARNINGS" AC_SUBST([AM_CFLAGS]) CPPFLAGS="-I\$(top_builddir)/make -I\$(top_srcdir)/make -I. $ENV_CPPFLAGS" LDFLAGS="$ENV_LDFLAGS" AM_EXTRA_RECURSIVE_TARGETS([xml-check xml-upload]) AX_PROG_DATE AS_IF([test "$ax_cv_prog_date_gnu_date:$ax_cv_prog_date_gnu_utc" = yes:yes], [UTC_DATE_AT="date -u -d@"], [AS_IF([test "x$ax_cv_prog_date_bsd_date" = xyes], [UTC_DATE_AT="date -u -r"], [AC_MSG_ERROR([date utility unable to convert epoch to UTC])])]) AC_SUBST([UTC_DATE_AT]) AC_ARG_VAR([SOURCE_EPOCH],[last modification date of the source]) AC_MSG_NOTICE([trying to determine source epoch]) AC_MSG_CHECKING([for source epoch in \$SOURCE_EPOCH]) AS_IF([test -n "$SOURCE_EPOCH"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch in source_epoch file]) AS_IF([test -e "$srcdir/source_epoch"], [read SOURCE_EPOCH <"$srcdir/source_epoch" AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch baked in by gitattributes export-subst]) SOURCE_EPOCH='$Format:%at$' # template for rewriting by git-archive AS_CASE([$SOURCE_EPOCH], [?Format:*], # was not rewritten [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch in \$SOURCE_DATE_EPOCH]) AS_IF([test "x$SOURCE_DATE_EPOCH" != x], [SOURCE_EPOCH="$SOURCE_DATE_EPOCH" AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([whether git log can provide a source epoch]) SOURCE_EPOCH=f${SOURCE_EPOCH#\$F} # convert into git log --pretty format SOURCE_EPOCH=$(cd "$srcdir" && git log -1 --pretty=${SOURCE_EPOCH%$} 2>/dev/null) AS_IF([test -n "$SOURCE_EPOCH"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no, using current time and breaking reproducibility]) SOURCE_EPOCH=$(date +%s)])])], [AC_MSG_RESULT([yes])] )]) ]) AC_MSG_NOTICE([using source epoch $($UTC_DATE_AT$SOURCE_EPOCH +'%F %T %Z')]) if test "x$VERSION" = "xUNKNOWN"; then AC_MSG_ERROR([m4_text_wrap([ configure was unable to determine the source tree's current version. This generally happens when using git archive (or the github download button) generated tarball/zip file. In order to workaround this issue, either use git clone https://github.com/ClusterLabs/fence-virt.git or use an official release tarball. Alternatively you can add a compatible version in a .tarball-version file at the top of the source tree, wipe your autom4te.cache dir and generated configure, and rerun autogen.sh. ], [ ], [ ], [76])]) fi AC_CONFIG_FILES([Makefile fence-agents.pc agents/Makefile lib/Makefile doc/Makefile agents/virt/Makefile agents/virt/config/Makefile agents/virt/common/Makefile agents/virt/client/Makefile agents/virt/server/Makefile agents/virt/man/Makefile ]) AC_OUTPUT