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>,
"Kadam, Pallavi" <pallavi.kadam@intel.com>,
Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>,
Thomas Monjalon <thomas@monjalon.net>,
Olivier Matz <olivier.matz@6wind.com>
Subject: [dpdk-dev] [PATCH v2 6/7] cmdline: support Windows
Date: Fri, 31 Jul 2020 00:06:50 +0300 [thread overview]
Message-ID: <20200730210652.14568-7-dmitry.kozliuk@gmail.com> (raw)
In-Reply-To: <20200730210652.14568-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 cff8b33dd..2d1b6fab2 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -270,6 +270,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 6bbaf242a..8430057c6 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -41,6 +41,7 @@ if is_windows
'eal',
'ring',
'mempool', 'mbuf', 'pci', 'net',
+ 'cmdline',
] # only supported libraries for windows
endif
--
2.25.4
next prev parent reply other threads:[~2020-07-30 21:08 UTC|newest]
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 ` [dpdk-dev] [PATCH 6/7] cmdline: support Windows Dmitry Kozlyuk
2020-06-28 14:20 ` 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 ` Dmitry Kozlyuk [this message]
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 publicly 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=20200730210652.14568-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=pallavi.kadam@intel.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
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).