diff --git a/dlls/kernel32/console.c b/dlls/kernel32/console.c index 9ac701883f2..aa7f1056d04 100644 --- a/dlls/kernel32/console.c +++ b/dlls/kernel32/console.c @@ -42,6 +42,8 @@ # include #endif +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winnls.h" @@ -146,6 +148,96 @@ static BOOL get_console_mode(HANDLE conin, DWORD* mode, BOOL* bare) return ret; } +static struct termios S_termios; /* saved termios for bare consoles */ +static BOOL S_termios_raw /* = FALSE */; + +/* The scheme for bare consoles for managing raw/cooked settings is as follows: + * - a bare console is created for all CUI programs started from command line (without + * wineconsole) (let's call those PS) + * - of course, every child of a PS which requires console inheritance will get it + * - the console termios attributes are saved at the start of program which is attached to be + * bare console + * - if any program attached to a bare console requests input from console, the console is + * turned into raw mode + * - when the program which created the bare console (the program started from command line) + * exits, it will restore the console termios attributes it saved at startup (this + * will put back the console into cooked mode if it had been put in raw mode) + * - if any other program attached to this bare console is still alive, the Unix shell will put + * it in the background, hence forbidding access to the console. Therefore, reading console + * input will not be available when the bare console creator has died. + * FIXME: This is a limitation of current implementation + */ + +/* returns the fd for a bare console (-1 otherwise) */ +static int get_console_bare_fd(HANDLE hin) +{ + BOOL is_bare; + int fd; + + if (get_console_mode(hin, NULL, &is_bare) && is_bare && + wine_server_handle_to_fd(hin, 0, &fd, NULL) == STATUS_SUCCESS) + return fd; + return -1; +} + +static BOOL save_console_mode(HANDLE hin) +{ + int fd; + BOOL ret; + + if ((fd = get_console_bare_fd(hin)) == -1) return FALSE; + ret = tcgetattr(fd, &S_termios) >= 0; + close(fd); + return ret; +} + +static BOOL put_console_into_raw_mode(HANDLE hin) +{ + int fd; + + if ((fd = get_console_bare_fd(hin)) == -1) return FALSE; + + RtlEnterCriticalSection(&CONSOLE_CritSect); + if (!S_termios_raw) + { + struct termios term = S_termios; + + term.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); + term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + term.c_cflag &= ~(CSIZE | PARENB); + term.c_cflag |= CS8; + /* FIXME: we should actually disable output processing here + * and let kernel32/console.c do the job (with support of enable/disable of + * processed output) + */ + /* term.c_oflag &= ~(OPOST); */ + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + S_termios_raw = tcsetattr(fd, TCSANOW, &term) >= 0; + } + RtlLeaveCriticalSection(&CONSOLE_CritSect); + + close(fd); + return S_termios_raw; +} + +/* put back the console in cooked mode iff we're the process which created the bare console + * we don't test if thie process has set the console in raw mode as it could be one of its + * child who did it + */ +static BOOL restore_console_mode(HANDLE hin) +{ + int fd; + BOOL ret; + + if (RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle != KERNEL32_CONSOLE_SHELL) + return TRUE; + if ((fd = get_console_bare_fd(hin)) == -1) return FALSE; + ret = tcsetattr(fd, TCSANOW, &S_termios) >= 0; + close(fd); + return ret; +} + /****************************************************************************** * GetConsoleWindow [KERNEL32.@] Get hwnd of the console window. * @@ -1080,6 +1172,7 @@ static enum read_console_input_return read_console_input(HANDLE handle, PINPUT_R if (bare) { + put_console_into_raw_mode(handle); if (WaitForSingleObject(GetConsoleInputWaitHandle(), 0) != WAIT_OBJECT_0) { ret = bare_console_fetch_input(handle, timeout); @@ -2893,6 +2986,7 @@ DWORD WINAPI GetConsoleProcessList(LPDWORD processlist, DWORD processcount) BOOL CONSOLE_Init(RTL_USER_PROCESS_PARAMETERS *params) { + memset(&S_termios, 0, sizeof(S_termios)); if (params->ConsoleHandle == KERNEL32_CONSOLE_SHELL) { HANDLE conin; @@ -2956,7 +3050,10 @@ BOOL CONSOLE_Init(RTL_USER_PROCESS_PARAMETERS *params) if (!params->hStdInput) params->hStdInput = INVALID_HANDLE_VALUE; else if (VerifyConsoleIoHandle(console_handle_map(params->hStdInput))) + { params->hStdInput = console_handle_map(params->hStdInput); + save_console_mode(params->hStdInput); + } if (!params->hStdOutput) params->hStdOutput = INVALID_HANDLE_VALUE; @@ -2970,3 +3067,9 @@ BOOL CONSOLE_Init(RTL_USER_PROCESS_PARAMETERS *params) return TRUE; } + +BOOL CONSOLE_Exit(void) +{ + /* the console is in raw mode, put it back in cooked mode */ + return restore_console_mode(GetStdHandle(STD_INPUT_HANDLE)); +} diff --git a/dlls/kernel32/kernel_main.c b/dlls/kernel32/kernel_main.c index 41647dd60cd..42d038e7bfb 100644 --- a/dlls/kernel32/kernel_main.c +++ b/dlls/kernel32/kernel_main.c @@ -138,6 +138,7 @@ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) return process_attach( hinst ); case DLL_PROCESS_DETACH: WritePrivateProfileSectionW( NULL, NULL, NULL ); + CONSOLE_Exit(); break; } return TRUE; diff --git a/dlls/kernel32/kernel_private.h b/dlls/kernel32/kernel_private.h index f741a92c294..f180e7d0580 100644 --- a/dlls/kernel32/kernel_private.h +++ b/dlls/kernel32/kernel_private.h @@ -29,6 +29,7 @@ HANDLE WINAPI DuplicateConsoleHandle(HANDLE, DWORD, BOOL, DWORD); BOOL WINAPI CloseConsoleHandle(HANDLE handle); HANDLE WINAPI GetConsoleInputWaitHandle(void); BOOL CONSOLE_Init(RTL_USER_PROCESS_PARAMETERS *params); +BOOL CONSOLE_Exit(void); static inline BOOL is_console_handle(HANDLE h) { diff --git a/server/console.c b/server/console.c index 2ca6bd41919..6616bd2d3b0 100644 --- a/server/console.c +++ b/server/console.c @@ -28,9 +28,6 @@ #include #include #include -#ifdef HAVE_TERMIOS_H -#include -#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -66,7 +63,6 @@ struct console_input user_handle_t win; /* window handle if backend supports it */ struct event *event; /* event to wait on for input queue */ struct fd *fd; /* for bare console, attached input fd */ - struct termios termios; /* for bare console, saved termio info */ }; static void console_input_dump( struct object *obj, int verbose ); @@ -328,38 +324,12 @@ static struct object *create_console_input( struct thread* renderer, int fd ) } if (fd != -1) /* bare console */ { - struct termios term; - if (!(console_input->fd = create_anonymous_fd( &console_fd_ops, fd, &console_input->obj, FILE_SYNCHRONOUS_IO_NONALERT ))) { release_object( console_input ); return NULL; } - if (tcgetattr(fd, &term) < 0) - { - release_object( console_input ); - set_error( STATUS_INVALID_HANDLE ); - return NULL; - } - console_input->termios = term; - term.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); - term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - term.c_cflag &= ~(CSIZE | PARENB); - term.c_cflag |= CS8; - /* FIXME: we should actually disable output processing here - * and let kernel32/console.c do the job (with support of enable/disable of - * processed output) - */ - /* term.c_oflag &= ~(OPOST); */ - term.c_cc[VMIN] = 1; - term.c_cc[VTIME] = 0; - if (tcsetattr(fd, TCSANOW, &term) < 0) - { - release_object( console_input ); - set_error( STATUS_INVALID_HANDLE ); - return NULL; - } allow_fd_caching( console_input->fd ); } @@ -1129,10 +1099,7 @@ static void console_input_destroy( struct object *obj ) } release_object( console_in->event ); if (console_in->fd) - { - tcsetattr(get_unix_fd(console_in->fd), TCSANOW, &console_in->termios); release_object( console_in->fd ); - } for (i = 0; i < console_in->history_size; i++) free( console_in->history[i] );