From patchwork Mon Sep 28 21:50:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Kozlyuk X-Patchwork-Id: 79064 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id D7773A04C0; Mon, 28 Sep 2020 23:53:29 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id F10B11C125; Mon, 28 Sep 2020 23:51:48 +0200 (CEST) Received: from mail-lf1-f65.google.com (mail-lf1-f65.google.com [209.85.167.65]) by dpdk.org (Postfix) with ESMTP id 1C94F1D6F0 for ; Mon, 28 Sep 2020 23:51:39 +0200 (CEST) Received: by mail-lf1-f65.google.com with SMTP id x69so3095233lff.3 for ; Mon, 28 Sep 2020 14:51:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=gJsvihyRYlQxFWK5X665Nth8+B0RVz6AwTC0bcoTQBw=; b=EmIi5WkKTGF2tX9Felh/wfZEBotCQvuisMWxHQHf7Qh0xYgZQY3NOBUdQcDHjG8AiK RDq8OFR/JA+/nzHl2L7HGVFMYWqKPnVQkz15glCdJOZdIzeMM4wkW04euynsHgab2IF+ I9vTAMlx6coNyDxZ7mh2KNUUYpwzL1By0z0B2jHvmMqfmhDdhxrCg0wOuI3+vy6Q5xh+ qvzHF8Sq+7q1R85xTs75e/CHKF0hG/ZcQAqr0V611djQ6qy3w3MnQrc37MDx4yTx82ct Ynhncw0IQ0qk9lr0Jj2t53XU6SzE3bq7P5vWEn8bCFHp2qvmvlf5WKi3BM2/S8BaEKOE NiEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=gJsvihyRYlQxFWK5X665Nth8+B0RVz6AwTC0bcoTQBw=; b=e97qIkOh2YgRJ4SxdxttvkV1ZBrJkkGqMhAElsSXhC5oFr59udA9bbmaHcKgT6IxGi KE2xy0aAXz90nnXjrHUXxfpaFSzx/4Bct5CPFbLYg90pNH2oNnJFpvgUEZChg/LeKX6l 84oD/gFwd65WHhX08kGn2GlSoQ5He9/kG5Q2HxEKC0QexO41S8KmMd9j01WWQeooyuZj cPaQT+8A8OhU6+T+ymfq/E0ZjCuSHUMeNTCO6ea7TQLjNRs7nLmPZTQpyfcv2ZA1hSvQ y5ri+11wFOva55+wlMOSa/jHHMLJzHT7ScF1uidFjxXOQ5oYJ367VjHH7x5sRmQLhO3E 8deg== X-Gm-Message-State: AOAM530qqrWoj3oqzktX85XnTmLRk72fzXIUAlq8DV2BHJuIQekU/Yqq I9aU0V3LvI2jroXSe7GqhP3S2OWumOGRc2pW X-Google-Smtp-Source: ABdhPJw2BlORms+iGITCOgEfZPbMmSNKYWZrVXX91lZLWPbkcnoM2VpUoaCnrQ8hWIOhRrdBhUO3Mw== X-Received: by 2002:ac2:4d05:: with SMTP id r5mr72433lfi.267.1601329898221; Mon, 28 Sep 2020 14:51:38 -0700 (PDT) Received: from localhost.localdomain (broadband-37-110-65-23.ip.moscow.rt.ru. [37.110.65.23]) by smtp.gmail.com with ESMTPSA id l11sm2954893lfk.113.2020.09.28.14.51.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 28 Sep 2020 14:51:37 -0700 (PDT) From: Dmitry Kozlyuk To: dev@dpdk.org Cc: "Kinsella, Ray" , Khoa To , Stephen Hemminger , Ferruh Yigit , Dmitry Kozlyuk , Bruce Richardson , Olivier Matz Date: Tue, 29 Sep 2020 00:50:51 +0300 Message-Id: <20200928215052.23627-7-dmitry.kozliuk@gmail.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200928215052.23627-1-dmitry.kozliuk@gmail.com> References: <20200730210652.14568-1-dmitry.kozliuk@gmail.com> <20200928215052.23627-1-dmitry.kozliuk@gmail.com> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH v3 6/7] cmdline: support Windows X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" 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 input 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 add a workaround. Signed-off-by: Dmitry Kozlyuk --- config/meson.build | 2 + lib/librte_cmdline/cmdline.c | 5 + lib/librte_cmdline/cmdline_os_windows.c | 207 ++++++++++++++++++++++++ lib/librte_cmdline/cmdline_parse.c | 2 +- lib/librte_cmdline/cmdline_private.h | 23 +++ lib/librte_cmdline/cmdline_socket.c | 4 + lib/librte_cmdline/cmdline_vt100.h | 4 + lib/librte_cmdline/meson.build | 4 +- lib/meson.build | 1 + 9 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 lib/librte_cmdline/cmdline_os_windows.c diff --git a/config/meson.build b/config/meson.build index 69f2aeb60..bd382bd7b 100644 --- a/config/meson.build +++ b/config/meson.build @@ -276,6 +276,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 49770869b..dd6229b56 100644 --- a/lib/librte_cmdline/cmdline.c +++ b/lib/librte_cmdline/cmdline.c @@ -13,11 +13,16 @@ #include #include #include +#include #include #include "cmdline_private.h" +#ifdef RTE_EXEC_ENV_WINDOWS +#define write _write +#endif + static void cmdline_valid_buffer(struct rdline *rdl, const char *buf, __rte_unused unsigned int size) diff --git a/lib/librte_cmdline/cmdline_os_windows.c b/lib/librte_cmdline/cmdline_os_windows.c new file mode 100644 index 000000000..e9585c9ee --- /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 + +#include + +#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 cmdline *cl) +{ + HANDLE handle; + DWORD mode; + + ZeroMemory(&cl->oldterm, sizeof(cl->oldterm)); + + /* Detect console input, set it up and make it emulate VT100. */ + handle = GetStdHandle(STD_INPUT_HANDLE); + if (GetConsoleMode(handle, &mode)) { + cl->oldterm.is_console_input = 1; + cl->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)) { + cl->oldterm.is_console_output = 1; + cl->oldterm.output_mode = mode; + + mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT; + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(handle, mode); + } +} + +void +terminal_restore(const struct cmdline *cl) +{ + if (cl->oldterm.is_console_input) { + HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); + SetConsoleMode(handle, cl->oldterm.input_mode); + } + + if (cl->oldterm.is_console_output) { + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleMode(handle, cl->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_parse.c b/lib/librte_cmdline/cmdline_parse.c index f120f19dd..fe366841c 100644 --- a/lib/librte_cmdline/cmdline_parse.c +++ b/lib/librte_cmdline/cmdline_parse.c @@ -15,7 +15,7 @@ #include -#include "cmdline.h" +#include "cmdline_private.h" #ifdef RTE_LIBRTE_CMDLINE_DEBUG #define debug_printf printf diff --git a/lib/librte_cmdline/cmdline_private.h b/lib/librte_cmdline/cmdline_private.h index 4d9ea33f0..a8a6ee9e6 100644 --- a/lib/librte_cmdline/cmdline_private.h +++ b/lib/librte_cmdline/cmdline_private.h @@ -8,9 +8,32 @@ #include #include +#ifdef RTE_EXEC_ENV_WINDOWS +#include +#endif #include +#ifdef RTE_EXEC_ENV_WINDOWS +struct terminal { + DWORD input_mode; + DWORD output_mode; + int is_console_input; + int is_console_output; +}; + +struct cmdline { + int s_in; + int s_out; + cmdline_parse_ctx_t *ctx; + struct rdline rdl; + char prompt[RDLINE_PROMPT_SIZE]; + struct terminal oldterm; + char repeated_char; + WORD repeat_count; +}; +#endif + /* Disable buffering and echoing, save previous settings to oldterm. */ void terminal_adjust(struct cmdline *cl); diff --git a/lib/librte_cmdline/cmdline_socket.c b/lib/librte_cmdline/cmdline_socket.c index 998e8ade2..0fe149700 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 d8b358e5f..ed66590a7 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -42,6 +42,7 @@ if is_windows 'eal', 'ring', 'mempool', 'mbuf', 'net', 'meter', 'ethdev', 'pci', + 'cmdline', ] # only supported libraries for windows endif