* In general, harmonize the debugserver named pipe with the other named
pipe instances: don't let remote users log messages and fail if the
pipe already exists.
* Set the PIPE_REJECT_REMOTE_CLIENTS flag on Vista and up.
* Set a DACL on named pipes that gives full control to the built-in
administrators, the local system account, and the current security
token's owner SID. This is the same DACL that is used by default,
except that the default also grants read access to the Everyone group
and the anonymous account.
* Also define WINVER=0x0501, because MinGW's sddl.h only defines
ConvertSidToStringSidW and ConvertStringSidToSidW if WINVER is defined.
(Ordinarily, defining _WIN32_WINNT is sufficient, and it's even
sufficient with the i686-pc-mingw32-g++ compiler packaged with Cygwin.)
* The createSecurityDescriptorOwnerFullControlEveryoneWrite function is
not currently used (or tested), but I think I'll use it in the debug
server to allow collecting trace output from other accounts on the
machine. (I think I'll make that behavior optional.)
I tested this commit with all of the supported compilers: MSYS1 MinGW,
Cygwin MinGW, MSYS2 MinGW-w64, Cygwin MinGW-w64, MSVC2013, and MSVC2015.
The code compiles and runs in all of them. I also examined the DACL
attached to the control pipe, and its SDDL string looked correct.
* PIDs are recycled, so include the system time.
* A program running on the same machine could predict the names of pipes
winpty uses and block winpty, causing a denial-of-service. I'm not sure
this is really a concern, but I suppose in principle it is? Try to
guard against it by appending random bytes to the pipe name. The
CreatePrivateNamespace API looks relevant, but I'm not sure it applies
to named pipes, and it's Vista only.
This bug was already fixed 5 months ago, but AFAICT MinGW.org hasn't
released it yet. MinGW-w64 is unaffected, as is the MinGW compiler
packaged with Cygwin.
The new API improves upon the old API in a number of ways:
* The old API provided a single data pipe for input and output, and it
provided the pipe in the form of a HANDLE opened with
FILE_FLAG_OVERLAPPED. Using a bare HANDLE is difficult in higher-level
languages, and overlapped/asynchronous I/O is hard to get right.
winpty_close closed the data pipe, which also didn't help things, though
I think the handle could have been duplicated.
Using a single handle for input and output complicates shutdown. When
the child process exits, the agent scrapes the final output, then closes
the data pipe once its written. It's possible that a winpty client will
first detect the closed pipe when it's writing *input* rather than
reading output, which seems wrong. (On the other hand, the agent
doesn't implement "backpressure" for either input or output (yet), and
if it did, it's possible that post-exit shutdown should interrupt a
blocked write into the console input queue. I need to think about it
more.)
The new API splits the data pipe into CONIN and CONOUT pipes, which are
accessed by filename. After `winpty_open` returns, the client queries
pipe names using `winpty_conin_name` and `winpty_conout_name`, then
opens them using any mechanism, low-level or high-level, blocking or
overlapped.
(There are still many open design issues in this part of the API.)
* The old libwinpty handled errors by killing the process. The new
libwinpty uses C++ exceptions internally and translates exceptions at
API boundaries using:
- a boolean error result (e.g. BOOL, NULL-vs-non-NULL)
- a potentially heap-allocated winpty_error_t object returned via an
optional winpty_error_ptr_t parameter. That parameter can be NULL.
If it isn't, then an error code and message can be queried from the
error object. The winpty_error_t object must be freed with
winpty_error_free.
* winpty_start_process is renamed to winpty_spawn. winpty_open and
winpty_spawn accept a "config" object which holds parameters. New
configuration options can be added without disturbing the source or
binary API. There are various flags I'm planning to add, which are
currently documented but not implemented.
* The winpty_get_exit_code and winpty_get_process_id APIs are removed.
The winpty_spawn function has an out parameter providing the child's
process and thread HANDLEs (duplicated from the agent via
DuplicateHandle). The winpty client can call GetExitCodeProcess and
GetProcessId (as well as the WaitForXXX APIs to wait for child exit).
As of this libwinpty rewrite, all code outside the UNIX adapter uses a
subset of C++11. The code will build with MSVC2013 or newer. The oldest
MinGW G++ I see on my machine is the somewhat broken MinGW (not MinGW-w64)
compiler distributed with Cygwin. That compiler is G++ 4.7.3, which is
new enough.
This is useful when winpty-agent is invoked from the Win32-OpenSSH server,
which uses a proper console for STDIN, but sockets(!) for STDOUT and
STDERR. The C++ runtime uses block buffering on the socket output, so
flush explicitly.
gyp tolerates all of:
'user32'
'user32.lib'
'-luser32'
'-luser32.lib'
With the --format=make generator, the exact string value is passed to gcc,
which only accepts the '-luser32' syntax. The MSVC generator is also
happy with this format, so that's what I'll use.
With this change, it's possible to use the gyp file to build the C++
components. I'm not sure if that's useful, but why not? The resulting
Makefile can be configured by passing settings like CXX and LDFLAGS on the
make command-line.
Also: document use of gyp a bit.
* Add a src/configurations.gypi file that can be included on the gyp
command-line to enable 32-bit and 64-bit builds in the generated project
files.
* Make winpty.gyp work if gyp is run with Cygwin python. It still works
with normal python.
* Explicitly call the Unicode versions of various APIs
* Add const in a couple of places
* Improve a few assert error messages
* Either initialize BackgroundDesktop fully or leave all its fields NULL
* winpty-agent: Respect the ENABLE_QUICK_EDIT_MODE flag. If it's set,
then mouse events are not generated (because they're supposed to control
selection).
* winpty-agent: Because of the above, turn ENABLE_QUICK_EDIT_MODE off at
startup.
* winpty-agent --show-input: Enable ENABLE_WINDOW_INPUT so we can see
buffer resize events.
* winpty-agent --show-input: Disable ENABLE_QUICK_EDIT_MODE with
--with-mouse. When Quick Edit mode is enabled, the mouse controls
selection and does not produce mouse events in the input queue.
Details:
* Rename --showkey to --show-input and dump everything *except* mouse
input.
* Add a mode, --show-input --with-mouse, that also dumps the mouse
input.
Now that the InputHandler thread blocks with select, in addition to the
main thread that also blocks with select, winpty has hit a bug in the
original MSYS where select returns EAGAIN. select is not supposed to fail
with that error (EAGAIN doesn't make much sense as a select error). Add
a workaround just for the original MSYS.
Explicitly shut down the I/O handling threads before restoring the terminal
state. Start calling winpty_close.
This change doesn't fix any bug, AFAIK. It's nice to test/demonstrate the
use of winpty_close.
The InputHandler now uses select to wait on both the blocking stdin tty and
a wakeup fd. This is the same behavior I saw in OpenSSH. OpenSSH sets
its select FDs to O_NONBLOCK *except* FDs for which isatty returns true.
It puts tty FDs into raw mode, but it does not set VMIN/VTIME to 0/0, so
the terminal driver blocks. (In any case, I tested the 0/0 values, and
IIRC they prevented blocking only with a Cygwin emulated pty -- console FDs
in Cygwin blocked; as did both consoles and emulated ptys in the original,
old MSYS. O_NONBLOCK prevented blocking in all cases, though.)
Terminals output these codes under various circumstances:
* rxvt-unicode: numpad, hold down SHIFT
* rxvt: numpad, by default
* xterm: numpad, after enabling app-mode using DECPAM (`ESC =`). xterm
generates `ESC O <mod> <letter>` for modified numpad presses,
necessitating kBareMod.
* mintty: by combining Ctrl with various keys such as '1' or ','.
Handling those keys is difficult, because mintty is generating the
same sequence for Ctrl-1 and Ctrl-NumPadEnd -- should the virtualKey
be '1' or VK_HOME?