DPDK patches and discussions
 help / color / Atom feed
From: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
To: dev@dpdk.org
Cc: Dmitry Malloy <dmitrym@microsoft.com>,
	Narcisa Ana Maria Vasile <Narcisa.Vasile@microsoft.com>,
	Fady Bader <fady@mellanox.com>,
	Tal Shnaiderman <talshn@mellanox.com>,
	Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>,
	Thomas Monjalon <thomas@monjalon.net>,
	Olivier Matz <olivier.matz@6wind.com>
Subject: [dpdk-dev] [PATCH 6/7] cmdline: support Windows
Date: Sun, 21 Jun 2020 00:05:09 +0300
Message-ID: <20200620210511.13134-7-dmitry.kozliuk@gmail.com> (raw)
In-Reply-To: <20200620210511.13134-1-dmitry.kozliuk@gmail.com>

Implement terminal handling, input polling, and vdprintf() for Windows.

Because Windows I/O model differs fundamentally from Unix and there is
no concept of character device, polling is simulated depending on the
underlying inpue device. Supporting non-terminal input is useful for
automated testing.

Windows emulation of VT100 uses "ESC [ E" for newline instead of
standard "ESC E", so a workaround is added.

Signed-off-by: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
---
 config/meson.build                      |   2 +
 lib/librte_cmdline/cmdline.c            |   5 +
 lib/librte_cmdline/cmdline_os_windows.c | 207 ++++++++++++++++++++++++
 lib/librte_cmdline/cmdline_private.h    |  15 ++
 lib/librte_cmdline/cmdline_socket.c     |   4 +
 lib/librte_cmdline/cmdline_vt100.h      |   4 +
 lib/librte_cmdline/meson.build          |   4 +-
 lib/meson.build                         |   1 +
 8 files changed, 241 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_cmdline/cmdline_os_windows.c

diff --git a/config/meson.build b/config/meson.build
index d3f05f878..733b5e310 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -269,6 +269,8 @@ if is_windows
 		add_project_arguments('-D__USE_MINGW_ANSI_STDIO', language: 'c')
 	endif
 
+	add_project_link_arguments('-lws2_32', language: 'c')
+
 	# Contrary to docs, VirtualAlloc2() is exported by mincore.lib
 	# in Windows SDK, while MinGW exports it by advapi32.a.
 	if is_ms_linker
diff --git a/lib/librte_cmdline/cmdline.c b/lib/librte_cmdline/cmdline.c
index 00b9e6b2e..c0ddb5f23 100644
--- a/lib/librte_cmdline/cmdline.c
+++ b/lib/librte_cmdline/cmdline.c
@@ -13,9 +13,14 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <netinet/in.h>
+#include <unistd.h>
 
 #include <rte_string_fns.h>
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+#define write _write
+#endif
+
 #include "cmdline.h"
 #include "cmdline_private.h"
 
diff --git a/lib/librte_cmdline/cmdline_os_windows.c b/lib/librte_cmdline/cmdline_os_windows.c
new file mode 100644
index 000000000..9736f6531
--- /dev/null
+++ b/lib/librte_cmdline/cmdline_os_windows.c
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Dmitry Kozlyuk
+ */
+
+#include <io.h>
+
+#include <rte_os.h>
+
+#include "cmdline_private.h"
+
+/* Missing from some MinGW-w64 distributions. */
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
+#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
+#endif
+
+void
+terminal_adjust(struct terminal *oldterm)
+{
+	HANDLE handle;
+	DWORD mode;
+
+	ZeroMemory(oldterm, sizeof(*oldterm));
+
+	/* Detect console input, set it up and make it emulate VT100. */
+	handle = GetStdHandle(STD_INPUT_HANDLE);
+	if (GetConsoleMode(handle, &mode)) {
+		oldterm->is_console_input = 1;
+		oldterm->input_mode = mode;
+
+		mode &= ~(
+			ENABLE_LINE_INPUT |      /* no line buffering */
+			ENABLE_ECHO_INPUT |      /* no echo */
+			ENABLE_PROCESSED_INPUT | /* pass Ctrl+C to program */
+			ENABLE_MOUSE_INPUT |     /* no mouse events */
+			ENABLE_WINDOW_INPUT);    /* no window resize events */
+		mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
+		SetConsoleMode(handle, mode);
+	}
+
+	/* Detect console output and make it emulate VT100. */
+	handle = GetStdHandle(STD_OUTPUT_HANDLE);
+	if (GetConsoleMode(handle, &mode)) {
+		oldterm->is_console_output = 1;
+		oldterm->output_mode = mode;
+
+		mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
+		mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+		SetConsoleMode(handle, mode);
+	}
+}
+
+void
+terminal_restore(const struct terminal *oldterm)
+{
+	if (oldterm->is_console_input) {
+		HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
+		SetConsoleMode(handle, oldterm->input_mode);
+	}
+
+	if (oldterm->is_console_output) {
+		HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
+		SetConsoleMode(handle, oldterm->output_mode);
+	}
+}
+
+static int
+cmdline_is_key_down(const INPUT_RECORD *record)
+{
+	return (record->EventType == KEY_EVENT) &&
+		record->Event.KeyEvent.bKeyDown;
+}
+
+static int
+cmdline_poll_char_console(HANDLE handle)
+{
+	INPUT_RECORD record;
+	DWORD events;
+
+	if (!PeekConsoleInput(handle, &record, 1, &events)) {
+		/* Simulate poll(3) behavior on EOF. */
+		return (GetLastError() == ERROR_HANDLE_EOF) ? 1 : -1;
+	}
+
+	if ((events == 0) || !cmdline_is_key_down(&record))
+		return 0;
+
+	return 1;
+}
+
+static int
+cmdline_poll_char_file(struct cmdline *cl, HANDLE handle)
+{
+	DWORD type = GetFileType(handle);
+
+	/* Since console is handled by cmdline_poll_char_console(),
+	 * this is either a serial port or input handle had been replaced.
+	 */
+	if (type == FILE_TYPE_CHAR)
+		return cmdline_poll_char_console(handle);
+
+	/* PeekNamedPipe() can handle all pipes and also sockets. */
+	if (type == FILE_TYPE_PIPE) {
+		DWORD bytes_avail;
+		if (!PeekNamedPipe(handle, NULL, 0, NULL, &bytes_avail, NULL))
+			return (GetLastError() == ERROR_BROKEN_PIPE) ? 1 : -1;
+		return bytes_avail ? 1 : 0;
+	}
+
+	/* There is no straightforward way to peek a file in Windows
+	 * I/O model. Read the byte, if it is not the end of file,
+	 * buffer it for subsequent read. This will not work with
+	 * a file being appended and probably some other edge cases.
+	 */
+	if (type == FILE_TYPE_DISK) {
+		char c;
+		int ret;
+
+		ret = _read(cl->s_in, &c, sizeof(c));
+		if (ret == 1) {
+			cl->repeat_count = 1;
+			cl->repeated_char = c;
+		}
+		return ret;
+	}
+
+	/* GetFileType() failed or file of unknown type,
+	 * which we do not know how to peek anyway.
+	 */
+	return -1;
+}
+
+int
+cmdline_poll_char(struct cmdline *cl)
+{
+	HANDLE handle = (HANDLE)_get_osfhandle(cl->s_in);
+	return cl->oldterm.is_console_input ?
+		cmdline_poll_char_console(handle) :
+		cmdline_poll_char_file(cl, handle);
+}
+
+ssize_t
+cmdline_read_char(struct cmdline *cl, char *c)
+{
+	HANDLE handle;
+	INPUT_RECORD record;
+	KEY_EVENT_RECORD *key;
+	DWORD events;
+
+	if (!cl->oldterm.is_console_input)
+		return _read(cl->s_in, c, 1);
+
+	/* Return repeated strokes from previous event. */
+	if (cl->repeat_count > 0) {
+		*c = cl->repeated_char;
+		cl->repeat_count--;
+		return 1;
+	}
+
+	handle = (HANDLE)_get_osfhandle(cl->s_in);
+	key = &record.Event.KeyEvent;
+	do {
+		if (!ReadConsoleInput(handle, &record, 1, &events)) {
+			if (GetLastError() == ERROR_HANDLE_EOF) {
+				*c = EOF;
+				return 0;
+			}
+			return -1;
+		}
+	} while (!cmdline_is_key_down(&record));
+
+	*c = key->uChar.AsciiChar;
+
+	/* Save repeated strokes from a single event. */
+	if (key->wRepeatCount > 1) {
+		cl->repeated_char = *c;
+		cl->repeat_count = key->wRepeatCount - 1;
+	}
+
+	return 1;
+}
+
+int
+cmdline_vdprintf(int fd, const char *format, va_list op)
+{
+	int copy, ret;
+	FILE *file;
+
+	copy = _dup(fd);
+	if (copy < 0)
+		return -1;
+
+	file = _fdopen(copy, "a");
+	if (file == NULL) {
+		_close(copy);
+		return -1;
+	}
+
+	ret = vfprintf(file, format, op);
+
+	fclose(file); /* also closes copy */
+
+	return ret;
+}
diff --git a/lib/librte_cmdline/cmdline_private.h b/lib/librte_cmdline/cmdline_private.h
index 338d3d55c..1e05ec376 100644
--- a/lib/librte_cmdline/cmdline_private.h
+++ b/lib/librte_cmdline/cmdline_private.h
@@ -5,7 +5,11 @@
 #ifndef _CMDLINE_PRIVATE_H_
 #define _CMDLINE_PRIVATE_H_
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+#include <rte_windows.h>
+#else
 #include <termios.h>
+#endif
 
 #include <stdarg.h>
 
@@ -15,7 +19,14 @@
 #include <cmdline_parse.h>
 
 struct terminal {
+#ifndef RTE_EXEC_ENV_WINDOWS
 	struct termios termios;
+#else
+	DWORD input_mode;
+	DWORD output_mode;
+	int is_console_input;
+	int is_console_output;
+#endif
 };
 
 /* Disable buffering and echoing, save previous settings to oldterm. */
@@ -31,6 +42,10 @@ struct cmdline {
 	struct rdline rdl;
 	char prompt[RDLINE_PROMPT_SIZE];
 	struct terminal oldterm;
+#ifdef RTE_EXEC_ENV_WINDOWS
+	char repeated_char;
+	WORD repeat_count;
+#endif
 };
 
 /* Check if a single character can be read from input. */
diff --git a/lib/librte_cmdline/cmdline_socket.c b/lib/librte_cmdline/cmdline_socket.c
index e73666f15..c5f483413 100644
--- a/lib/librte_cmdline/cmdline_socket.c
+++ b/lib/librte_cmdline/cmdline_socket.c
@@ -16,6 +16,10 @@
 #include "cmdline_private.h"
 #include "cmdline_socket.h"
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+#define open _open
+#endif
+
 struct cmdline *
 cmdline_file_new(cmdline_parse_ctx_t *ctx, const char *prompt, const char *path)
 {
diff --git a/lib/librte_cmdline/cmdline_vt100.h b/lib/librte_cmdline/cmdline_vt100.h
index e33e67ed8..be9ae8e1c 100644
--- a/lib/librte_cmdline/cmdline_vt100.h
+++ b/lib/librte_cmdline/cmdline_vt100.h
@@ -31,7 +31,11 @@ extern "C" {
 #define vt100_multi_right  "\033\133%uC"
 #define vt100_multi_left   "\033\133%uD"
 #define vt100_suppr        "\033\133\063\176"
+#ifndef RTE_EXEC_ENV_WINDOWS
 #define vt100_home         "\033M\033E"
+#else
+#define vt100_home         "\033M\033[E"
+#endif
 #define vt100_word_left    "\033\142"
 #define vt100_word_right   "\033\146"
 
diff --git a/lib/librte_cmdline/meson.build b/lib/librte_cmdline/meson.build
index 5c9e8886d..5009b3354 100644
--- a/lib/librte_cmdline/meson.build
+++ b/lib/librte_cmdline/meson.build
@@ -25,7 +25,9 @@ headers = files('cmdline.h',
 	'cmdline_cirbuf.h',
 	'cmdline_parse_portlist.h')
 
-if not is_windows
+if is_windows
+	sources += files('cmdline_os_windows.c')
+else
 	sources += files('cmdline_os_unix.c')
 endif
 
diff --git a/lib/meson.build b/lib/meson.build
index 40e452025..5b72a2d9e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -40,6 +40,7 @@ if is_windows
 		'kvargs','eal',
 		'ring',
 		'mempool', 'mbuf', 'pci', 'net',
+		'cmdline',
 	] # only supported libraries for windows
 endif
 
-- 
2.25.4


  parent reply index

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-20 21:05 [dpdk-dev] [PATCH 0/7] " Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 1/7] cmdline: make implementation opaque Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 2/7] cmdline: add internal wrappers for terminal handling Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 3/7] cmdline: add internal wrappers for character input Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 4/7] cmdline: add internal wrapper for vdprintf Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 5/7] eal/windows: improve compatibility networking headers Dmitry Kozlyuk
2020-06-20 21:05 ` Dmitry Kozlyuk [this message]
2020-06-28 14:20   ` [dpdk-dev] [PATCH 6/7] cmdline: support Windows Fady Bader
2020-06-29  6:23     ` Ranjit Menon
2020-06-29  7:42       ` Dmitry Kozlyuk
2020-06-29  8:12         ` Tal Shnaiderman
2020-06-29 23:56           ` Dmitry Kozlyuk
2020-07-08  1:09             ` Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 7/7] examples/cmdline: build on Windows Dmitry Kozlyuk
2020-07-17 22:16 ` [dpdk-dev] [PATCH 0/7] cmdline: support Windows Narcisa Ana Maria Vasile
2020-07-30 18:08 ` Kadam, Pallavi
2020-07-30 21:06 ` [dpdk-dev] [PATCH v2 " Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 1/7] cmdline: make implementation opaque Dmitry Kozlyuk
2020-08-05  9:31     ` Kinsella, Ray
2020-08-05 11:17       ` Dmitry Kozlyuk
2020-09-30  8:11         ` Kinsella, Ray
2020-09-30 15:26           ` Dmitry Kozlyuk
2020-09-17 13:34     ` Olivier Matz
2020-09-17 17:05       ` Stephen Hemminger
2020-09-18  8:33         ` Bruce Richardson
2020-09-18 12:13           ` Ferruh Yigit
2020-09-18 13:23         ` Kinsella, Ray
2020-09-17 23:13       ` Dmitry Kozlyuk
2020-09-18 13:31       ` Kinsella, Ray
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 2/7] cmdline: add internal wrappers for terminal handling Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 3/7] cmdline: add internal wrappers for character input Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 4/7] cmdline: add internal wrapper for vdprintf Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 5/7] eal/windows: improve compatibility networking headers Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 6/7] cmdline: support Windows Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 7/7] examples/cmdline: build on Windows Dmitry Kozlyuk
2020-09-26  1:33     ` [dpdk-dev] [EXTERNAL] " Narcisa Ana Maria Vasile
2020-09-26  6:03       ` Khoa To
2020-09-28 21:50   ` [dpdk-dev] [PATCH v3 0/7] cmdline: support Windows Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 1/7] cmdline: make implementation logically opaque Dmitry Kozlyuk
2020-09-30  8:12       ` Kinsella, Ray
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 2/7] cmdline: add internal wrappers for terminal handling Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 3/7] cmdline: add internal wrappers for character input Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 4/7] cmdline: add internal wrapper for vdprintf Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 5/7] eal/windows: improve compatibility networking headers Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 6/7] cmdline: support Windows Dmitry Kozlyuk
2020-10-14 22:31       ` Thomas Monjalon
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 7/7] examples/cmdline: build on Windows Dmitry Kozlyuk
2020-10-14 22:33       ` Thomas Monjalon
2020-10-05 15:33     ` [dpdk-dev] [PATCH v3 0/7] cmdline: support Windows Olivier Matz
2020-10-14 22:41       ` Thomas Monjalon

Reply instructions:

You may reply publically to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200620210511.13134-7-dmitry.kozliuk@gmail.com \
    --to=dmitry.kozliuk@gmail.com \
    --cc=Narcisa.Vasile@microsoft.com \
    --cc=dev@dpdk.org \
    --cc=dmitrym@microsoft.com \
    --cc=fady@mellanox.com \
    --cc=olivier.matz@6wind.com \
    --cc=talshn@mellanox.com \
    --cc=thomas@monjalon.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

DPDK patches and discussions

Archives are clonable:
	git clone --mirror http://inbox.dpdk.org/dev/0 dev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 dev dev/ http://inbox.dpdk.org/dev \
		dev@dpdk.org
	public-inbox-index dev


Newsgroup available over NNTP:
	nntp://inbox.dpdk.org/inbox.dpdk.dev


AGPL code for this site: git clone https://public-inbox.org/ public-inbox