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