Initial checkin of serial server-side support

Signed-off-by: Lon Hohberger <lon@users.sourceforge.net>
This commit is contained in:
Lon Hohberger 2010-01-13 02:19:24 -05:00
parent 4ae121d860
commit 30e8654e77
6 changed files with 617 additions and 73 deletions

View File

@ -176,6 +176,8 @@ hangup(int fd, int delay)
}
}
void do_read_hostlist(int fd, int timeout);
int
serial_fence_virt(fence_virt_args_t *args)
@ -188,7 +190,7 @@ serial_fence_virt(fence_virt_args_t *args)
strncpy(speed, args->serial.speed, sizeof(speed));
printf("Port: %s Speed: %s\n", args->serial.device, speed);
//printf("Port: %s Speed: %s\n", args->serial.device, speed);
if ((flags = strchr(speed, ','))) {
*flags = 0;
@ -208,8 +210,8 @@ serial_fence_virt(fence_virt_args_t *args)
req.request = (uint8_t)args->op;
if (args->flags & RF_UUID)
req.flags |= RF_UUID;
strncpy((char *)req.domain, args->domain, sizeof(req.domain));
if (args->domain)
strncpy((char *)req.domain, args->domain, sizeof(req.domain));
tv.tv_sec = 3;
tv.tv_usec = 0;
@ -229,15 +231,12 @@ serial_fence_virt(fence_virt_args_t *args)
if (resp.magic != SERIAL_MAGIC)
return -1;
ret = (int)resp.response;
/* XXX try a response from netcat: e.g.
abba1<cr> = fail; abba0<cr>=ok
should be removed when we have "real"
server side handling; this was just to
test communications */
if (ret & 0x30)
ret &= (~0x30);
ret = resp.response;
if (resp.response == 253) /* hostlist */ {
/* ok read hostlist */
do_read_hostlist(fd, args->timeout);
ret = 0;
}
close(fd);

View File

@ -43,7 +43,7 @@ libvirt_qpid_so_SOURCES = uuid-test.c
libvirt_qpid_cxx_so_SOURCES = libvirt-qpid.cpp
multicast_so_SOURCES = mcast.c history.c
checkpoint_so_SOURCES = virt.c vm_states.c history.c checkpoint.c cpg.c
serial_so_SOURCES = virt-serial.c uuid-test.c
serial_so_SOURCES = virt-serial.c uuid-test.c virt-sockets.c serial.c history.c
################################

380
server/serial.c Normal file
View File

@ -0,0 +1,380 @@
/*
Copyright Red Hat, Inc. 2010
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
MA 02139, USA.
*/
/*
* Author: Lon Hohberger <lhh at redhat.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <nss.h>
#include <libgen.h>
#include <list.h>
#include <simpleconfig.h>
#include <server_plugin.h>
#include <history.h>
#include <xvm.h>
/* Local includes */
#include "debug.h"
#include "fdops.h"
#include "virt-sockets.h"
#define NAME "serial"
#define VERSION "0.3"
#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;
history_info_t *history;
} serial_info;
struct serial_hostlist_arg {
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
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;
strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain));
strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid));
hinfo.state = state;
tv.tv_sec = 1;
tv.tv_usec = 0;
printf("%d\n", arg->fd);
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 = 253;
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, serial_req_t *req, serial_info *info)
{
char response = 1;
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:
response = info->cb->on((char *)req->domain, req->seqno,
info->priv);
break;
case FENCE_OFF:
response = info->cb->off((char *)req->domain, req->seqno,
info->priv);
break;
case FENCE_REBOOT:
response = info->cb->reboot((char *)req->domain, req->seqno,
info->priv);
break;
case FENCE_STATUS:
response = info->cb->status((char *)req->domain, info->priv);
break;
case FENCE_DEVSTATUS:
response = info->cb->devstatus(info->priv);
break;
case FENCE_HOSTLIST:
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;
dbg_printf(3, "Sending response to caller...\n");
if (write(fd, &resp, sizeof(resp)) < 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)
{
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);
n = select(max+1, &rfds, NULL, NULL, timeout);
printf("n = %d max = %d\n", n, max);
if (n < 0) {
perror("select");
return n;
}
/*
* 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);
printf("the read...%d\n",ret);
if (ret != sizeof(data)) {
if (--n)
continue;
else
return 0;
} else {
break;
}
}
}
printf("Sock %d Request %d seqno %d domain %s\n", x, data.request, data.seqno,
data.domain);
if (history_check(info->history, &data) == 1) {
printf("We just did this request; dropping packet\n");
return 0;
}
do_fence_request(x, &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);
}
return errors;
}
static int
serial_init(listener_context_t *c, const fence_callbacks_t *cb,
config_object_t *config, 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->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);
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();
free(info->uri);
free(info);
return 0;
}
static listener_plugin_t serial_plugin = {
.name = NAME,
.version = 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

View File

@ -5,6 +5,7 @@
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/poll.h>
@ -12,26 +13,41 @@
#include <libxml/xmlreader.h>
#ifdef DEBUG
#define DEBUG0(fmt) printf("%s:%d :: " fmt "\n", \
__func__, __LINE__)
#define DEBUG(fmt, ...) printf("%s:%d: " fmt "\n", \
#define DEBUG1(fmt, ...) printf("%s:%d: " fmt "\n", \
__func__, __LINE__, __VA_ARGS__)
#else
#define DEBUG0(fmt)
#define DEBUG1(fmt, ...)
#endif
#include "virt-sockets.h"
#define STREQ(a,b) (strcmp((a),(b)) == 0)
/* handle globals */
int h_fd = 0;
virEventHandleType h_event = 0;
virEventHandleCallback h_cb = NULL;
virFreeCallback h_ff = NULL;
void *h_opaque = NULL;
static int h_fd = -1;
static virEventHandleType h_event = 0;
static virEventHandleCallback h_cb = NULL;
static virFreeCallback h_ff = NULL;
static void *h_opaque = NULL;
/* timeout globals */
#define TIMEOUT_MS 1000
int t_active = 0;
int t_timeout = -1;
virEventTimeoutCallback t_cb = NULL;
virFreeCallback t_ff = NULL;
void *t_opaque = NULL;
static int t_active = 0;
static int t_timeout = -1;
static virEventTimeoutCallback t_cb = NULL;
static virFreeCallback t_ff = NULL;
static void *t_opaque = NULL;
static pthread_t event_tid = 0;
static int run = 0;
/* Prototypes */
const char *eventToString(int event);
@ -116,19 +132,19 @@ myEventAddHandleFunc(int fd, int event,
virEventHandleCallback cb,
void *opaque, virFreeCallback ff)
{
DEBUG("Add handle %d %d %p %p", fd, event, cb, opaque);
DEBUG1("Add handle %d %d %p %p %p", fd, event, cb, opaque, ff);
h_fd = fd;
h_event = myEventHandleTypeToPollEvent(event);
h_cb = cb;
h_ff = ff;
h_opaque = opaque;
h_ff = ff;
return 0;
}
void
myEventUpdateHandleFunc(int fd, int event)
{
DEBUG("Updated Handle %d %d", fd, event);
DEBUG1("Updated Handle %d %d", fd, event);
h_event = myEventHandleTypeToPollEvent(event);
return;
}
@ -136,7 +152,7 @@ myEventUpdateHandleFunc(int fd, int event)
int
myEventRemoveHandleFunc(int fd)
{
DEBUG("Removed Handle %d", fd);
DEBUG1("Removed Handle %d", fd);
h_fd = 0;
if (h_ff)
(h_ff) (h_opaque);
@ -148,7 +164,7 @@ myEventAddTimeoutFunc(int timeout,
virEventTimeoutCallback cb,
void *opaque, virFreeCallback ff)
{
DEBUG("Adding Timeout %d %p %p", timeout, cb, opaque);
DEBUG1("Adding Timeout %d %p %p", timeout, cb, opaque);
t_active = 1;
t_timeout = timeout;
t_cb = cb;
@ -160,36 +176,20 @@ myEventAddTimeoutFunc(int timeout,
void
myEventUpdateTimeoutFunc(int timer, int timeout)
{
/*DEBUG("Timeout updated %d %d", timer, timeout); */
/*DEBUG1("Timeout updated %d %d", timer, timeout); */
t_timeout = timeout;
}
int
myEventRemoveTimeoutFunc(int timer)
{
DEBUG("Timeout removed %d", timer);
DEBUG1("Timeout removed %d", timer);
t_active = 0;
if (t_ff)
(t_ff) (t_opaque);
return 0;
}
/* main test functions */
void
usage(const char *pname)
{
printf("%s uri\n", pname);
}
int run = 1;
static void
stop(int sig)
{
printf("Exiting on signal %d\n", sig);
run = 0;
}
static int
domainStarted(virDomainPtr mojaDomain)
@ -268,6 +268,8 @@ domainStarted(virDomainPtr mojaDomain)
printf(">> REGISTER >> %s %s\n",
dom_uuid,
attr_path->children->content);
domain_sock_setup(dom_uuid, (const char *)
attr_path->children->content);
}
}
}
@ -345,24 +347,21 @@ domainStopped(virDomainPtr mojaDomain)
printf("UUID: %s\n", dom_uuid);
printf(">> UNREGISTER >> %s\n", dom_uuid);
domain_sock_close(dom_uuid);
return 0;
}
int
main(int argc, char **argv)
static void *
event_thread(void *arg)
{
virConnectPtr dconn = NULL;
struct domain_info dinfo;
struct sigaction action_stop = {.sa_handler = stop };
int sts;
int callback1ret = -1;
if (argc > 1 && STREQ(argv[1], "--help")) {
usage(argv[0]);
return -1;
}
printf("Event listener starting \n");
virEventRegisterImpl(myEventAddHandleFunc,
myEventUpdateHandleFunc,
@ -371,15 +370,12 @@ main(int argc, char **argv)
myEventUpdateTimeoutFunc,
myEventRemoveTimeoutFunc);
dconn = virConnectOpen(argv[1] ? argv[1] : NULL);
dconn = virConnectOpen((char *)arg);
if (!dconn) {
printf("error opening\n");
return -1;
return NULL;
}
sigaction(SIGTERM, &action_stop, NULL);
sigaction(SIGINT, &action_stop, NULL);
DEBUG0("Registering domain event cbs");
registerExisting(dconn);
@ -398,6 +394,12 @@ main(int argc, char **argv)
.revents = 0
};
sts = poll(&pfd, 1, TIMEOUT_MS);
/* We are assuming timeout of 0 here - so execute every time */
if (t_cb && t_active) {
t_cb(t_timeout, t_opaque);
}
if (dinfo.event == VIR_DOMAIN_EVENT_STARTED) {
domainStarted(dinfo.dom);
virDomainFree(dinfo.dom);
@ -410,39 +412,31 @@ main(int argc, char **argv)
dinfo.event = VIR_DOMAIN_EVENT_UNDEFINED;
}
sts = poll(&pfd, 1, TIMEOUT_MS);
/* We are assuming timeout of 0 here - so execute every time */
if (t_cb && t_active) {
t_cb(t_timeout, t_opaque);
}
if (sts == 0) {
/* DEBUG0("Poll timeout"); */
continue;
}
if (sts < 0) {
DEBUG0("Poll failed");
continue;
}
if (pfd.revents & POLLHUP) {
DEBUG0("Reset by peer");
return -1;
return NULL;
}
if (h_cb) {
h_cb(0,
h_fd,
myPollEventToEventHandleType(pfd.
revents &
h_cb(0, h_fd,
myPollEventToEventHandleType(pfd.revents &
h_event),
h_opaque);
}
}
DEBUG0("Deregistering event handlers");
virConnectDomainEventDeregister(dconn, myDomainEventCallback1);
}
DEBUG0("Closing connection");
@ -451,5 +445,38 @@ main(int argc, char **argv)
}
printf("done\n");
return NULL;
}
int
start_event_listener(const char *uri)
{
char *arg = NULL;
virInitialize();
if (uri) {
arg = strdup(uri);
if (uri == NULL)
return -1;
}
run = 1;
return pthread_create(&event_tid, NULL, event_thread, arg);
}
int
stop_event_listener(void)
{
run = 0;
pthread_cancel(event_tid);
pthread_join(event_tid, NULL);
event_tid = 0;
return 0;
}

128
server/virt-sockets.c Normal file
View File

@ -0,0 +1,128 @@
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <list.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include "virt-sockets.h"
struct socket_list {
list_head();
char *domain_name;
char *socket_path;
int socket_fd;
};
static struct socket_list *socks = NULL;
static pthread_mutex_t sock_list_mutex = PTHREAD_MUTEX_INITIALIZER;
int
domain_sock_setup(const char *domain, const char *socket_path)
{
struct sockaddr_un *sun = NULL;
struct socket_list *node = NULL;
socklen_t sun_len;
int sock = -1;
sun_len = sizeof(*sun) + strlen(socket_path) + 1;
sun = malloc(sun_len);
if (!sun)
return -1;
memset((char *)sun, 0, sun_len);
sun->sun_family = PF_LOCAL;
strncpy(sun->sun_path, socket_path, sun_len - sizeof(*sun));
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0)
goto out_fail;
if (connect(sock, (struct sockaddr *)sun, SUN_LEN(sun)) < 0)
goto out_fail;
node = malloc(sizeof(*node));
if (!node)
goto out_fail;
node->domain_name = strdup(domain);
if (!node->domain_name)
goto out_fail;
node->socket_path = strdup(socket_path);
if (!node->socket_path)
goto out_fail;
node->socket_fd = sock;
pthread_mutex_lock(&sock_list_mutex);
list_insert(&socks, node);
pthread_mutex_unlock(&sock_list_mutex);
printf("REGISTERED %s on %d\n", domain, sock);
return 0;
out_fail:
if (node)
free(node->domain_name);
if (node)
free(node->socket_path);
free(node);
free(sun);
if (sock >= 0)
close(sock);
return -1;
}
int
domain_sock_close(const char *domain)
{
struct socket_list *node = NULL;
struct socket_list *dead = NULL;
int x;
pthread_mutex_lock(&sock_list_mutex);
list_for(&socks, node, x) {
if (!strcasecmp(domain, node->domain_name)) {
list_remove(&socks, node);
dead = node;
break;
}
}
pthread_mutex_unlock(&sock_list_mutex);
if (dead) {
close(dead->socket_fd);
free(dead->domain_name);
free(dead->socket_path);
}
return 0;
}
int
domain_sock_fdset(fd_set *fds, int *max)
{
struct socket_list *node = NULL;
int x = 0, _max = -1;
pthread_mutex_lock(&sock_list_mutex);
list_for(&socks, node, x) {
FD_SET(node->socket_fd, fds);
if (node->socket_fd > _max)
_max = node->socket_fd;
}
pthread_mutex_unlock(&sock_list_mutex);
if (max)
*max = _max;
return x;
}

10
server/virt-sockets.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _VIRT_SOCKETS_H
#define _VIRT_SOCKETS_H
#include <sys/select.h>
int domain_sock_setup(const char *domain, const char *socket_path);
int domain_sock_close(const char *domain);
int domain_sock_fdset(fd_set *set, int *max);
#endif