From ac899b10a60e315a9062e6a5933679d8d3d4548f Mon Sep 17 00:00:00 2001 From: Marsell Kukuljevic Date: Thu, 1 Nov 2018 21:09:19 +1300 Subject: [PATCH] TOOLS-2077 port modern mdata tools to Windows --- Makefile | 6 ++ README.md | 26 ++++++-- plat/cygwin.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 plat/cygwin.c diff --git a/Makefile b/Makefile index af0c297..a0b4a79 100644 --- a/Makefile +++ b/Makefile @@ -98,6 +98,12 @@ PLATFORM_OK = true MANSECT = 1 endif +ifeq ($(UNAME_S),CYGWIN_NT-6.3) +CFILES += plat/cygwin.c plat/unix_common.c +HDRS += plat/unix_common.h +PLATFORM_OK = true +endif + ifeq ($(PLATFORM_OK),false) $(error Unknown platform: $(UNAME_S)) endif diff --git a/README.md b/README.md index 3847e40..dd71c00 100644 --- a/README.md +++ b/README.md @@ -31,17 +31,29 @@ base metadata keys for guests to consume, as well as the ability to support arbitrary additional user-provided metadata. In a SmartOS container/zone guest, a UNIX domain socket is used to communicate -with the metadata server running in the hypervisor. In a KVM guest, such as a -Linux virtual machine, the client tools will make use of the second serial port -(e.g. `ttyb`, or `COM2`) to communicate with the hypervisor. +with the metadata server running in the hypervisor. In a bhyve or KVM guest, +such as a Linux virtual machine, the client tools will make use of the second +serial port (e.g. `ttyb`, or `COM2`) to communicate with the hypervisor. # OS Support -The tools currently build and function on SmartOS and various Linux -distributions. Support for other operating systems, such as \*BSD or Windows, -is absolutely welcome. +The tools currently build and function on SmartOS, some \*BSDs, various Linux +distributions, and Windows (cygwin). Support for other operating systems is +absolutely welcome. -## License +## Cygwin + +To build mdata executables on Cygwin, use the 64-bit Cygwin installer 2.893 or +later available at https://cygwin.com/install.html. Building requires binutils, +gcc-core and make, all under the Devel category. Invoke the Cygwin terminal, +and run make on this repo. + +make will produce several exe files. These programs can be copied to and run on +any version of Windows supported by Cygwin, so long as cygwin1.dll (found in +C:\cygwin\bin of the development machine's Cygwin install) is copied into the +same directory as the binaries. + +# License MIT (See _LICENSE_.) diff --git a/plat/cygwin.c b/plat/cygwin.c new file mode 100644 index 0000000..334b666 --- /dev/null +++ b/plat/cygwin.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019, Joyent, Inc. + * See LICENSE file for copyright and license details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "plat.h" +#include "dynstr.h" +#include "plat/unix_common.h" + +#define SERIAL_DEVICE "/dev/ttyS1" +#define MAX_READ_SZ 16 + +struct mdata_plat { + struct pollfd mpl_poll; + int mpl_conn; +}; + + +int +plat_send(mdata_plat_t *mpl, string_t *data) +{ + size_t len = dynstr_len(data); + + if (write(mpl->mpl_conn, dynstr_cstr(data), len) != (ssize_t)len) + return (-1); + + return (0); +} + +int +plat_recv(mdata_plat_t *mpl, string_t *data, int timeout_ms) +{ + for (;;) { + struct pollfd *mpl_poll = &mpl->mpl_poll; + short revents = 0; + + if (poll(mpl_poll, 1, timeout_ms) == -1) { + fprintf(stderr, "poll error: %d\n", errno); + if (errno == EINTR) + return (-1); + err(1, "POLL_WAIT ERROR"); + } + + revents = mpl_poll->revents; + + if (mpl_poll->fd < 0 || revents == 0) { + fprintf(stderr, "plat_recv timeout\n"); + return (-1); + } + + if (revents & POLLIN) { + char buf[MAX_READ_SZ + 1]; + static char buf_prev[MAX_READ_SZ + 1] = ""; + ssize_t sz; + + if ((sz = read(mpl_poll->fd, buf, MAX_READ_SZ)) > 0) { + boolean_t terminate = B_FALSE; + char* curr = buf; + char* rem = buf; + char* end = buf + sz; + + if (*buf_prev != '\0') { + dynstr_append(data, buf_prev); + *buf_prev = '\0'; + } + + while ((rem = memchr(curr, '\n', end - curr))) { + *rem = '\0'; + dynstr_append(data, curr); + curr = rem; + terminate = B_TRUE; + } + + if (terminate == B_TRUE) { + return (0); + } + + *end = '\0'; + strcpy(buf_prev, curr); + } + } + + if (revents & POLLERR) { + fprintf(stderr, "POLLERR\n"); + return (-1); + } + + if (revents & POLLHUP) { + fprintf(stderr, "POLLHUP\n"); + return (-1); + } + } + + return (-1); +} + +void +plat_fini(mdata_plat_t *mpl) +{ + if (mpl != NULL) { + if (mpl->mpl_conn != -1) + (void) close(mpl->mpl_conn); + free(mpl); + } +} + +static int +plat_send_reset(mdata_plat_t *mpl) +{ + int ret = -1; + string_t *str = dynstr_new(); + + dynstr_append(str, "\n"); + if (plat_send(mpl, str) != 0) + goto bail; + dynstr_reset(str); + + if (plat_recv(mpl, str, 2000) != 0) + goto bail; + + if (strcmp(dynstr_cstr(str), "invalid command") != 0) + goto bail; + + ret = 0; + +bail: + dynstr_free(str); + return (ret); +} + +int +plat_is_interactive(void) +{ + return (unix_is_interactive()); +} + +int +plat_init(mdata_plat_t **mplout, char **errmsg, int *permfail) +{ + mdata_plat_t *mpl = NULL; + + if ((mpl = calloc(1, sizeof (*mpl))) == NULL) { + *errmsg = "Could not allocate memory."; + *permfail = 1; + goto bail; + } + + if (unix_open_serial(SERIAL_DEVICE, &mpl->mpl_poll.fd, errmsg, + permfail) != 0) { + goto bail; + } + + mpl->mpl_conn = mpl->mpl_poll.fd; + mpl->mpl_poll.events = POLLIN | POLLERR | POLLHUP; + + if (plat_send_reset(mpl) == -1) { + *errmsg = "Could not do active reset."; + goto bail; + } + + *mplout = mpl; + + return (0); + +bail: + plat_fini(mpl); + return (-1); +}