* Use ASSERT() where the API is being misused. Also use it when
WaitForMultipleObjects or WriteFile return a value I don't expect to
ever see.
* Allow passing err to winpty_error_{code,msg}, though. Instead of
complaining about the NULL error pointer, instead return
WINPTY_ERROR_SUCCESS and L"Success".
* In agent: if the console is frozen, then calling abort hangs forever,
because abort tries to write to stderr, which blocks. Instead,
use WM_CLOSE to close the console window. If that doesn't work after
waiting a while, then exit.
We need to do the same thing doing normal agent shutdown, so factor out
an agentShutdown function. It's a bit unhappy living in
WinptyAssert.cc, but it works.
* Outside agent: trace an assertion failure message, then delegate to the
ordinary assert() function. If that's somehow turned off, then call
abort().
The gyp file already worked this way, but now the Makefile does too.
The motivation is to let the shared source files have different error
handling behavior (e.g. assert/ASSERT/stderr-print/abort/exception) in
winpty.dll and the other targets.
* The c99_snprintf function was not actually a C99-conformant snprintf
function, except perhaps when compiled for Cygwin/MSYS.
* MSVCRT.DLL exposes _vsnprintf, _vswprintf, _vscprintf, and _vscwprintf,
which are the four most relevant and fundamental printf functions. It
*doesn't* expose a function like _scprintf, even though MSDN documents
it. It seems that the simple wrapper functions are linked statically
even when linking dynamically to the MS CRT.
It appears that MinGW and MinGW-w64 generallY provide these four
functions in all configurations. Therefore, for predictable behavior,
always use these functions.
* Examples of non-predictability:
- MinGW declares _vscprintf but not _scprintf. MinGW-w64 declares
both.
- MinGW and MinGW-w64 sometimes override some printf functions with
their own versions. (It's easy to tell when this happens because
"%zd" starts working.) With MinGW, -std=c99 enables the overrides,
as does -std=c++11. -std=gnu++11 does NOT enable the overrides.
- When the overrides are not in effect, MinGW declares swprintf as
taking no size parameter. When the overrides ARE in effect, MinGW
does not declare swprintf. MinGW-w64 and MSVC prefer swprintf with
a size parameter, but they overload it in C++ mode to allow calling
it either way. Cygwin and ISO C require the size parameter.
- With overrides off, MinGW's snprintf is still the MinGW override
function. MinGW-w64's snprintf instead calls MSVCRT.DLL _snprintf.
MinGW-w64 changes its PRI??? strings when overrides are enabled, but
MinGW does not.
* In the native Windows mode, which I'm more interested in anyway, it's
fairly easy to calculate the length of a formatted string before
outputting it. Add a few wrapper functions that do this. I could
enhance StringBuilder with easy ways to add stdio-formatted strings.
Of course, these sizes are only valid if they're computed by the same
printf implementation that writes into the string, and they will be.
Work around a MinGW header-file non-idempotency bug w.r.t _vscwprintf
Currently, when a header file is removed (or renamed), there is a stale
dependency file still referring to the header, and GNU make aborts, because
the header is missing. Attempt to work around this by adding a pattern
rule to generate any header in `src/`. The rule prints a warning message.
The C source file whose header was missing will be recompiled because a
dependency was rebuilt. Once it is, the removed header should be removed
from the dependency file.
(Aside: FWIW, the "ninja" build tool knows directly about depfiles, and it
does not have this problem.)
* 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.