fence-virt/client/serial.c
Ryan McCabe f61626c108 Add a TCP listener plugin for use with viosproxy
Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
2012-06-01 15:22:02 -04:00

311 lines
5.4 KiB
C

/*
* 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 <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 <fdops.h>
#include <xvm.h>
#include <options.h>
#include <client.h>
#include <sys/time.h>
#include <arpa/inet.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;
}
}
void do_read_hostlist(int fd, int timeout);
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));
//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));
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;
}