summaryrefslogtreecommitdiffstats
path: root/agents/virt/client/serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'agents/virt/client/serial.c')
-rw-r--r--agents/virt/client/serial.c311
1 files changed, 311 insertions, 0 deletions
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;
+}