diff --git a/contrib/win32/openssh/config.h.vs b/contrib/win32/openssh/config.h.vs
index 7d5ea2ecf..bb5030ec1 100644
--- a/contrib/win32/openssh/config.h.vs
+++ b/contrib/win32/openssh/config.h.vs
@@ -297,7 +297,7 @@
/* Define to 1 if you have the declaration of `O_NONBLOCK', and to 0 if you
don't. */
-#define HAVE_DECL_O_NONBLOCK 0
+#define HAVE_DECL_O_NONBLOCK 1
/* Define to 1 if you have the declaration of `passwdexpired', and to 0 if you
don't. */
diff --git a/contrib/win32/openssh/keygen.vcxproj b/contrib/win32/openssh/keygen.vcxproj
index 690ec33da..ac196225a 100644
--- a/contrib/win32/openssh/keygen.vcxproj
+++ b/contrib/win32/openssh/keygen.vcxproj
@@ -1,4 +1,4 @@
-
+
@@ -186,6 +186,7 @@
+
@@ -193,4 +194,4 @@
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/keygen.vcxproj.filters b/contrib/win32/openssh/keygen.vcxproj.filters
index dd9dd237d..8c39fa3e6 100644
--- a/contrib/win32/openssh/keygen.vcxproj.filters
+++ b/contrib/win32/openssh/keygen.vcxproj.filters
@@ -1,4 +1,4 @@
-
+
@@ -21,10 +21,13 @@
Source Files
+
+ Source Files
+
Resource Files
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/scp.vcxproj b/contrib/win32/openssh/scp.vcxproj
index 7a9225452..1a473315d 100644
--- a/contrib/win32/openssh/scp.vcxproj
+++ b/contrib/win32/openssh/scp.vcxproj
@@ -1,4 +1,4 @@
-
+
@@ -22,6 +22,7 @@
+
@@ -191,4 +192,4 @@
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/scp.vcxproj.filters b/contrib/win32/openssh/scp.vcxproj.filters
index fccf50e03..e37843803 100644
--- a/contrib/win32/openssh/scp.vcxproj.filters
+++ b/contrib/win32/openssh/scp.vcxproj.filters
@@ -1,4 +1,4 @@
-
+
@@ -21,10 +21,13 @@
Source Files
+
+ Source Files
+
Resource Files
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/sftp.vcxproj b/contrib/win32/openssh/sftp.vcxproj
index b55803358..c7ffb6dac 100644
--- a/contrib/win32/openssh/sftp.vcxproj
+++ b/contrib/win32/openssh/sftp.vcxproj
@@ -1,4 +1,4 @@
-
+
@@ -26,6 +26,7 @@
+
@@ -197,4 +198,4 @@
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/sftp.vcxproj.filters b/contrib/win32/openssh/sftp.vcxproj.filters
index 04bc65203..60da32cc3 100644
--- a/contrib/win32/openssh/sftp.vcxproj.filters
+++ b/contrib/win32/openssh/sftp.vcxproj.filters
@@ -1,4 +1,4 @@
-
+
@@ -33,10 +33,13 @@
Source Files
+
+ Source Files
+
Resource Files
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/ssh.vcxproj b/contrib/win32/openssh/ssh.vcxproj
index d72793441..83c43367a 100644
--- a/contrib/win32/openssh/ssh.vcxproj
+++ b/contrib/win32/openssh/ssh.vcxproj
@@ -1,4 +1,4 @@
-
+
@@ -298,8 +298,9 @@
-
+
+
@@ -307,4 +308,4 @@
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/ssh.vcxproj.filters b/contrib/win32/openssh/ssh.vcxproj.filters
index 8acdd0e21..fae9a0fd9 100644
--- a/contrib/win32/openssh/ssh.vcxproj.filters
+++ b/contrib/win32/openssh/ssh.vcxproj.filters
@@ -1,4 +1,4 @@
-
+
@@ -311,10 +311,13 @@
Source Files
+
+ Source Files
+
Resource Files
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/win32iocompat.vcxproj b/contrib/win32/openssh/win32iocompat.vcxproj
index c2b0024b2..51c419992 100644
--- a/contrib/win32/openssh/win32iocompat.vcxproj
+++ b/contrib/win32/openssh/win32iocompat.vcxproj
@@ -160,7 +160,6 @@
-
diff --git a/contrib/win32/openssh/win32iocompat.vcxproj.filters b/contrib/win32/openssh/win32iocompat.vcxproj.filters
index 2152b5fae..456abfedd 100644
--- a/contrib/win32/openssh/win32iocompat.vcxproj.filters
+++ b/contrib/win32/openssh/win32iocompat.vcxproj.filters
@@ -19,7 +19,6 @@
-
diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c
index 49da1ad57..e699ac909 100644
--- a/contrib/win32/win32compat/fileio.c
+++ b/contrib/win32/win32compat/fileio.c
@@ -55,8 +55,9 @@ struct createFile_flags {
DWORD dwFlagsAndAttributes;
};
-int termio_initiate_read(struct w32_io* pio);
-int termio_initiate_write(struct w32_io* pio, DWORD num_bytes);
+int syncio_initiate_read(struct w32_io* pio);
+int syncio_initiate_write(struct w32_io* pio, DWORD num_bytes);
+int syncio_close(struct w32_io* pio);
/* maps Win32 error to errno */
int
@@ -440,11 +441,10 @@ fileio_read(struct w32_io* pio, void *dst, unsigned int max)
}
if (fileio_is_io_available(pio, TRUE) == FALSE) {
- if (FILETYPE(pio) == FILE_TYPE_CHAR) {
- if (-1 == termio_initiate_read(pio))
+ if (pio->type == NONSOCK_SYNC_FD || FILETYPE(pio) == FILE_TYPE_CHAR) {
+ if (-1 == syncio_initiate_read(pio))
return -1;
- }
- else {
+ } else {
if (-1 == fileio_ReadFileEx(pio, max)) {
if ((FILETYPE(pio) == FILE_TYPE_PIPE)
&& (errno == ERROR_BROKEN_PIPE)) {
@@ -571,46 +571,12 @@ fileio_write(struct w32_io* pio, const void *buf, unsigned int max)
bytes_copied = min(max, pio->write_details.buf_size);
memcpy(pio->write_details.buf, buf, bytes_copied);
- if (FILETYPE(pio) == FILE_TYPE_CHAR) {
- if (termio_initiate_write(pio, bytes_copied) == 0) {
+ if (pio->type == NONSOCK_SYNC_FD || FILETYPE(pio) == FILE_TYPE_CHAR) {
+ if (syncio_initiate_write(pio, bytes_copied) == 0) {
pio->write_details.pending = TRUE;
pio->write_details.remaining = bytes_copied;
- }
- else
+ } else
return -1;
- } else if ( FILETYPE(pio) == FILE_TYPE_PIPE &&
- GetNamedPipeInfo(WINHANDLE(pio), &pipe_flags, NULL, NULL, &pipe_instances) &&
- pipe_flags == PIPE_CLIENT_END && pipe_instances == 1) {
- /*
- * TODO - Figure out a better solution to this problem
- * IO handle corresponding to this object (pio->handle) may be referring
- * to something that isn't opened in overlapped mode. While all handles
- * opened by this POSIX wrapper are opened in overlapped mode, other handles
- * that are inherited (ex. via std i/o) are typically not.
- * Ex. When we do this in Powershell
- * $o = ssh.exe user@target hostname
- * Powershell creates anonymous pipes (that do not support overlapped i.o)
- * Calling asynchronous I/O APIs (WriteFileEx) for example will not work in
- * those cases (the callback is never called and it typically manifests as a
- * hang to end user
- *
- * This conditional logic is put in place to specifically handle Powershell
- * redirection scenarios. Thinking behind these conditions
- * - should be a pipe handle. console I/O is handled in termio.c, impacting file i/o
- * scenarios not found yet.
- * - pipe should be the client end. This is to skip pipes created internally in POSIX
- * wrapper (by pipe() calls) - The write ends on these pipes are on server
- * - pipe_instances == 1. This is to skip pipe handles created as part of Connect(AF_UNIX)
- * sockets (that typically are created for unlimited instances).
- * For such I/O we do a synchronous write.
- */
- /* DebugBreak() */;
- if (WriteFile(WINHANDLE(pio), pio->write_details.buf, bytes_copied, &bytes_copied, NULL) == FALSE) {
- errno = errno_from_Win32LastError();
- debug3("write - WriteFile() ERROR:%d, io:%p", GetLastError(), pio);
- return -1;
- }
- return bytes_copied;
} else {
if (WriteFileEx(WINHANDLE(pio), pio->write_details.buf, bytes_copied,
&pio->write_overlapped, &WriteCompletionRoutine)) {
@@ -753,8 +719,8 @@ fileio_on_select(struct w32_io* pio, BOOL rd)
if (!pio->read_details.pending && !fileio_is_io_available(pio, rd))
/* initiate read, record any error so read() will pick up */
- if (FILETYPE(pio) == FILE_TYPE_CHAR) {
- if (termio_initiate_read(pio) != 0) {
+ if (pio->type == NONSOCK_SYNC_FD || FILETYPE(pio) == FILE_TYPE_CHAR) {
+ if (syncio_initiate_read(pio) != 0) {
pio->read_details.error = errno;
errno = 0;
return;
@@ -773,6 +739,9 @@ fileio_close(struct w32_io* pio)
{
debug4("fileclose - pio:%p", pio);
+ if (pio->type == NONSOCK_SYNC_FD || FILETYPE(pio) == FILE_TYPE_CHAR)
+ return syncio_close(pio);
+
/* handle can be null on AF_UNIX sockets that are not yet connected */
if (WINHANDLE(pio) == 0 || WINHANDLE(pio) == INVALID_HANDLE_VALUE) {
free(pio);
@@ -782,15 +751,13 @@ fileio_close(struct w32_io* pio)
CancelIo(WINHANDLE(pio));
/* let queued APCs (if any) drain */
SleepEx(0, TRUE);
- if (pio->type != STD_IO_FD) { /* STD handles are never explicitly closed */
- CloseHandle(WINHANDLE(pio));
-
+ CloseHandle(WINHANDLE(pio));
+ /* free up non stdio */
+ if (!IS_STDIO(pio)) {
if (pio->read_details.buf)
free(pio->read_details.buf);
-
if (pio->write_details.buf)
free(pio->write_details.buf);
-
free(pio);
}
return 0;
diff --git a/contrib/win32/win32compat/inc/fcntl.h b/contrib/win32/win32compat/inc/fcntl.h
index 3f6514933..fba482ee4 100644
--- a/contrib/win32/win32compat/inc/fcntl.h
+++ b/contrib/win32/win32compat/inc/fcntl.h
@@ -22,7 +22,6 @@ int w32_open(const char *pathname, int flags, ...);
void* w32_fd_to_handle(int fd);
int w32_allocate_fd_for_handle(void* h, int is_sock);
-#define O_ACCMODE 0x0003
#define O_RDONLY _O_RDONLY
#define O_WRONLY _O_WRONLY
#define O_RDWR _O_RDWR
@@ -37,4 +36,26 @@ int w32_allocate_fd_for_handle(void* h, int is_sock);
#define O_NOINHERIT _O_NOINHERIT
#define O_SEQUENTIAL _O_SEQUENTIAL
#define O_RANDOM _O_RANDOM
-#define O_U16TEXT _O_U16TEXT
\ No newline at end of file
+#define O_U16TEXT _O_U16TEXT
+
+/*
+* open() POSIX specific modes and flags.
+* Caution while making changes
+* - cross check conflict with common macros in Windows headers
+* - Ex. #define O_APPEND 0x8
+*/
+#define O_ACCMODE 0x0003
+#define O_NONBLOCK 0x0004 /*io operations wont block*/
+# define S_IXUSR 0000100 /* execute/search permission, */
+# define S_IXGRP 0000010 /* execute/search permission, */
+# define S_IXOTH 0000001 /* execute/search permission, */
+# define _S_IWUSR 0000200 /* write permission, */
+# define S_IWUSR _S_IWUSR /* write permission, owner */
+# define S_IWGRP 0000020 /* write permission, group */
+# define S_IWOTH 0000002 /* write permission, other */
+# define S_IRUSR 0000400 /* read permission, owner */
+# define S_IRGRP 0000040 /* read permission, group */
+# define S_IROTH 0000004 /* read permission, other */
+# define S_IRWXU 0000700 /* read, write, execute */
+# define S_IRWXG 0000070 /* read, write, execute */
+# define S_IRWXO 0000007 /* read, write, execute */
\ No newline at end of file
diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c
index 93b1af394..99bbaf3bd 100644
--- a/contrib/win32/win32compat/misc.c
+++ b/contrib/win32/win32compat/misc.c
@@ -404,123 +404,6 @@ w32_ioctl(int d, int request, ...)
}
}
-/*
- * spawn a child process
- * - specified by cmd with agruments argv
- * - with std handles set to in, out, err
- * - flags are passed to CreateProcess call
- *
- * cmd will be internally decoarated with a set of '"'
- * to account for any spaces within the commandline
- * this decoration is done only when additional arguments are passed in argv
- */
-int
-spawn_child(char* cmd, char** argv, int in, int out, int err, DWORD flags)
-{
- PROCESS_INFORMATION pi;
- STARTUPINFOW si;
- BOOL b;
- char *cmdline, *t, **t1;
- DWORD cmdline_len = 0;
- wchar_t * cmdline_utf16;
- int add_module_path = 0, ret = -1;
-
- /* should module path be added */
- do {
- if (!cmd)
- break;
- t = cmd;
- if (*t == '\"')
- t++;
- if (t[0] == '\0' || t[0] == '\\' || t[0] == '.' || t[1] == ':')
- break;
- add_module_path = 1;
- } while (0);
-
- /* compute total cmdline len*/
- if (add_module_path)
- cmdline_len += strlen(w32_programdir()) + 1 + strlen(cmd) + 1 + 2;
- else
- cmdline_len += strlen(cmd) + 1 + 2;
-
- if (argv) {
- t1 = argv;
- while (*t1)
- cmdline_len += strlen(*t1++) + 1 + 2;
- }
-
- if ((cmdline = malloc(cmdline_len)) == NULL) {
- errno = ENOMEM;
- goto cleanup;
- }
-
- /* add current module path to start if needed */
- t = cmdline;
- if (argv && argv[0])
- *t++ = '\"';
- if (add_module_path) {
- memcpy(t, w32_programdir(), strlen(w32_programdir()));
- t += strlen(w32_programdir());
- *t++ = '\\';
- }
-
- memcpy(t, cmd, strlen(cmd));
- t += strlen(cmd);
-
- if (argv && argv[0])
- *t++ = '\"';
-
- if (argv) {
- t1 = argv;
- while (*t1) {
- *t++ = ' ';
- *t++ = '\"';
- memcpy(t, *t1, strlen(*t1));
- t += strlen(*t1);
- *t++ = '\"';
- t1++;
- }
- }
-
- *t = '\0';
-
- if ((cmdline_utf16 = utf8_to_utf16(cmdline)) == NULL) {
- errno = ENOMEM;
- goto cleanup;
- }
-
- memset(&si, 0, sizeof(STARTUPINFOW));
- si.cb = sizeof(STARTUPINFOW);
- si.hStdInput = w32_fd_to_handle(in);
- si.hStdOutput = w32_fd_to_handle(out);
- si.hStdError = w32_fd_to_handle(err);
- si.dwFlags = STARTF_USESTDHANDLES;
-
- debug3("spawning %ls", cmdline_utf16);
- b = CreateProcessW(NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
-
- if (b) {
- if (register_child(pi.hProcess, pi.dwProcessId) == -1) {
- TerminateProcess(pi.hProcess, 0);
- CloseHandle(pi.hProcess);
- goto cleanup;
- }
- CloseHandle(pi.hThread);
- } else {
- errno = GetLastError();
- goto cleanup;
- }
-
- ret = pi.dwProcessId;
-cleanup:
- if (cmdline)
- free(cmdline);
- if (cmdline_utf16)
- free(cmdline_utf16);
-
- return ret;
-}
-
void
strmode(mode_t mode, char *p)
{
diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h
index 6150de03a..fa9a0d7b5 100644
--- a/contrib/win32/win32compat/misc_internal.h
+++ b/contrib/win32/win32compat/misc_internal.h
@@ -1,5 +1,8 @@
#pragma once
#define PATH_MAX MAX_PATH
+#define SSH_ASYNC_STDIN "SSH_ASYNC_STDIN"
+#define SSH_ASYNC_STDOUT "SSH_ASYNC_STDOUT"
+#define SSH_ASYNC_STDERR "SSH_ASYNC_STDERR"
/* removes first '/' for Windows paths that are unix styled. Ex: /c:/ab.cd */
char * sanitized_path(const char *);
diff --git a/contrib/win32/win32compat/signal.c b/contrib/win32/win32compat/signal.c
index 035f79ce7..d0499031b 100644
--- a/contrib/win32/win32compat/signal.c
+++ b/contrib/win32/win32compat/signal.c
@@ -78,7 +78,7 @@ sigtstp_APCProc(_In_ ULONG_PTR dwParam)
BOOL WINAPI
native_sig_handler(DWORD dwCtrlType)
{
- debug3("Native Ctrl+C handler, CtrlType %d", dwCtrlType);
+ debug4("Native Ctrl+C handler, CtrlType %d", dwCtrlType);
switch (dwCtrlType) {
case CTRL_C_EVENT:
QueueUserAPC(sigint_APCProc, main_thread, (ULONG_PTR)NULL);
@@ -154,7 +154,7 @@ w32_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
int
w32_raise(int sig)
{
- debug3("raise sig:%d", sig);
+ debug4("raise sig:%d", sig);
if (sig == W32_SIGSEGV)
return raise(SIGSEGV); /* raise native exception handler*/
@@ -229,7 +229,7 @@ sw_process_pending_signals()
DebugBreak();
if (sig_int) {
- debug3("process_queued_signals: WARNING - A signal has interrupted and was processed");
+ debug4("process_queued_signals: WARNING - A signal has interrupted and was processed");
errno = EINTR;
return -1;
}
diff --git a/contrib/win32/win32compat/socketio.c b/contrib/win32/win32compat/socketio.c
index 804520dd0..5e9d6621f 100644
--- a/contrib/win32/win32compat/socketio.c
+++ b/contrib/win32/win32compat/socketio.c
@@ -110,7 +110,7 @@ socketio_acceptEx(struct w32_io* pio)
context = (struct acceptEx_context *)pio->internal.context;
ResetEvent(pio->read_overlapped.hEvent);
- if (getsockname(pio->sock, &addr, &addrlen) == SOCKET_ERROR) {
+ if (getsockname(pio->sock, (struct sockaddr*)&addr, &addrlen) == SOCKET_ERROR) {
errno = errno_from_WSALastError();
debug("acceptEx - getsockname() ERROR:%d, io:%p", errno, pio);
return -1;
diff --git a/contrib/win32/win32compat/termio.c b/contrib/win32/win32compat/termio.c
index 33ed14ef0..945e227ba 100644
--- a/contrib/win32/win32compat/termio.c
+++ b/contrib/win32/win32compat/termio.c
@@ -9,6 +9,10 @@
* Author: Balu
* Misc fixes and code cleanup
*
+ * Author: Manoj Ampalam
+ * Extended support to other Windows IO that does not support
+ * overlapped IO. Ex. pipe handles returned by CreatePipe()
+ *
* Copyright (c) 2017 Microsoft Corp.
* All rights reserved
*
@@ -71,18 +75,26 @@ ReadAPCProc(_In_ ULONG_PTR dwParam)
/* Read worker thread */
static DWORD WINAPI
-ReadConsoleThread(_In_ LPVOID lpParameter)
+ReadThread(_In_ LPVOID lpParameter)
{
int nBytesReturned = 0;
struct w32_io* pio = (struct w32_io*)lpParameter;
debug5("TermRead thread, io:%p", pio);
memset(&read_status, 0, sizeof(read_status));
- while (nBytesReturned == 0) {
- nBytesReturned = ReadConsoleForTermEmul(WINHANDLE(pio),
- pio->read_details.buf, pio->read_details.buf_size);
+ if (FILETYPE(pio) == FILE_TYPE_CHAR) {
+ while (nBytesReturned == 0) {
+ nBytesReturned = ReadConsoleForTermEmul(WINHANDLE(pio),
+ pio->read_details.buf, pio->read_details.buf_size);
+ }
+ read_status.transferred = nBytesReturned;
+ } else {
+ if (!ReadFile(WINHANDLE(pio), pio->read_details.buf,
+ pio->read_details.buf_size, &read_status.transferred, NULL)) {
+ read_status.error = GetLastError();
+ debug("ReadThread - ReadFile failed %d, io:%p", GetLastError(), pio);
+ }
}
- read_status.transferred = nBytesReturned;
if (0 == QueueUserAPC(ReadAPCProc, main_thread, (ULONG_PTR)pio)) {
debug3("TermRead thread - ERROR QueueUserAPC failed %d, io:%p", GetLastError(), pio);
pio->read_details.pending = FALSE;
@@ -95,11 +107,11 @@ ReadConsoleThread(_In_ LPVOID lpParameter)
/* Initiates read on tty */
int
-termio_initiate_read(struct w32_io* pio)
+syncio_initiate_read(struct w32_io* pio)
{
HANDLE read_thread;
- debug5("TermRead initiate io:%p", pio);
+ debug5("syncio_initiate_read io:%p", pio);
if (pio->read_details.buf_size == 0) {
pio->read_details.buf = malloc(TERM_IO_BUF_SIZE);
if (pio->read_details.buf == NULL) {
@@ -109,7 +121,7 @@ termio_initiate_read(struct w32_io* pio)
pio->read_details.buf_size = TERM_IO_BUF_SIZE;
}
- read_thread = CreateThread(NULL, 0, ReadConsoleThread, pio, 0, NULL);
+ read_thread = CreateThread(NULL, 0, ReadThread, pio, 0, NULL);
if (read_thread == NULL) {
errno = errno_from_Win32Error(GetLastError());
debug3("TermRead initiate - ERROR CreateThread %d, io:%p", GetLastError(), pio);
@@ -148,19 +160,26 @@ WriteThread(_In_ LPVOID lpParameter)
size_t resplen = 0;
debug5("TermWrite thread, io:%p", pio);
- pio->write_details.buf[write_status.to_transfer] = '\0';
-
- if (0 == in_raw_mode) {
- wchar_t* t = utf8_to_utf16(pio->write_details.buf);
- WriteConsoleW(WINHANDLE(pio), t, wcslen(t), 0, 0);
- free(t);
+ if (FILETYPE(pio) == FILE_TYPE_CHAR) {
+ pio->write_details.buf[write_status.to_transfer] = '\0';
+ if (0 == in_raw_mode) {
+ wchar_t* t = utf8_to_utf16(pio->write_details.buf);
+ WriteConsoleW(WINHANDLE(pio), t, wcslen(t), 0, 0);
+ free(t);
+ } else {
+ processBuffer(WINHANDLE(pio), pio->write_details.buf, write_status.to_transfer, &respbuf, &resplen);
+ /* TODO - respbuf is not null in some cases, this needs to be returned back via read stream */
+ }
+ write_status.transferred = write_status.to_transfer;
} else {
- processBuffer(WINHANDLE(pio), pio->write_details.buf, write_status.to_transfer, &respbuf, &resplen);
- /* TODO - respbuf is not null in some cases, this needs to be returned back via read stream */
+ if (!WriteFile(WINHANDLE(pio), pio->write_details.buf, write_status.to_transfer,
+ &write_status.transferred, NULL)) {
+ write_status.error = GetLastError();
+ debug("WriteThread - ReadFile WriteFile %d, io:%p", GetLastError(), pio);
+ }
}
- write_status.transferred = write_status.to_transfer;
-
+
if (0 == QueueUserAPC(WriteAPCProc, main_thread, (ULONG_PTR)pio)) {
debug3("TermWrite thread - ERROR QueueUserAPC failed %d, io:%p", GetLastError(), pio);
pio->write_details.pending = FALSE;
@@ -173,7 +192,7 @@ WriteThread(_In_ LPVOID lpParameter)
/* Initiates write on tty */
int
-termio_initiate_write(struct w32_io* pio, DWORD num_bytes)
+syncio_initiate_write(struct w32_io* pio, DWORD num_bytes)
{
HANDLE write_thread;
debug5("TermWrite initiate io:%p", pio);
@@ -193,21 +212,27 @@ termio_initiate_write(struct w32_io* pio, DWORD num_bytes)
/* tty close */
int
-termio_close(struct w32_io* pio)
+syncio_close(struct w32_io* pio)
{
- debug4("termio_close - pio:%p", pio);
+ debug4("syncio_close - pio:%p", pio);
HANDLE h;
CancelIoEx(WINHANDLE(pio), NULL);
- /* If io is pending, let write worker threads exit. The read thread is blocked so terminate it.*/
- if (pio->read_details.pending)
- TerminateThread(pio->read_overlapped.hEvent, 0);
+
+ /* If io is pending, let worker threads exit. */
+ if (pio->read_details.pending) {
+ /* For console - the read thread is blocked so terminate it. */
+ if (FILETYPE(pio) == FILE_TYPE_CHAR)
+ TerminateThread(pio->read_overlapped.hEvent, 0);
+ else
+ WaitForSingleObject(pio->read_overlapped.hEvent, INFINITE);
+ }
if (pio->write_details.pending)
WaitForSingleObject(pio->write_overlapped.hEvent, INFINITE);
/* drain queued APCs */
SleepEx(0, TRUE);
- if (pio->type != STD_IO_FD) {
- /* STD handles are never explicitly closed */
- CloseHandle(WINHANDLE(pio));
+ CloseHandle(WINHANDLE(pio));
+ /* free up if non stdio */
+ if (!IS_STDIO(pio)) {
if (pio->read_details.buf)
free(pio->read_details.buf);
if (pio->write_details.buf)
diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c
index 4e48f9644..3921fd44e 100644
--- a/contrib/win32/win32compat/w32fd.c
+++ b/contrib/win32/win32compat/w32fd.c
@@ -36,6 +36,7 @@
#include "inc\unistd.h"
#include "inc\fcntl.h"
#include "inc\sys\un.h"
+#include "inc\utf.h"
#include "w32fd.h"
#include "signal_internal.h"
@@ -74,15 +75,24 @@ fd_table_initialize()
memset(&fd_table, 0, sizeof(fd_table));
memset(&w32_io_stdin, 0, sizeof(w32_io_stdin));
w32_io_stdin.std_handle = STD_INPUT_HANDLE;
- w32_io_stdin.type = STD_IO_FD;
+ w32_io_stdin.type = NONSOCK_SYNC_FD;
+ if (getenv(SSH_ASYNC_STDIN) && strcmp(getenv(SSH_ASYNC_STDIN), "1") == 0)
+ w32_io_stdin.type = NONSOCK_FD;
+ _putenv_s(SSH_ASYNC_STDIN, "");
fd_table_set(&w32_io_stdin, STDIN_FILENO);
memset(&w32_io_stdout, 0, sizeof(w32_io_stdout));
w32_io_stdout.std_handle = STD_OUTPUT_HANDLE;
- w32_io_stdout.type = STD_IO_FD;
+ w32_io_stdout.type = NONSOCK_SYNC_FD;
+ if (getenv(SSH_ASYNC_STDOUT) && strcmp(getenv(SSH_ASYNC_STDOUT), "1") == 0)
+ w32_io_stdout.type = NONSOCK_FD;
+ _putenv_s(SSH_ASYNC_STDOUT, "");
fd_table_set(&w32_io_stdout, STDOUT_FILENO);
memset(&w32_io_stderr, 0, sizeof(w32_io_stderr));
w32_io_stderr.std_handle = STD_ERROR_HANDLE;
- w32_io_stderr.type = STD_IO_FD;
+ w32_io_stderr.type = NONSOCK_SYNC_FD;
+ if (getenv(SSH_ASYNC_STDERR) && strcmp(getenv(SSH_ASYNC_STDERR), "1") == 0)
+ w32_io_stderr.type = NONSOCK_FD;
+ _putenv_s(SSH_ASYNC_STDERR, "");
fd_table_set(&w32_io_stderr, STDERR_FILENO);
return 0;
}
@@ -128,7 +138,6 @@ fd_table_set(struct w32_io* pio, int index)
static void
fd_table_clear(int index)
{
- fd_table.w32_ios[index]->table_index = -1;
fd_table.w32_ios[index] = NULL;
FD_CLR(index, &(fd_table.occupied));
}
@@ -483,6 +492,7 @@ int
w32_close(int fd)
{
struct w32_io* pio;
+ int r;
if ((fd < 0) || (fd > MAX_FDS - 1) || fd_table.w32_ios[fd] == NULL) {
errno = EBADF;
return -1;
@@ -492,17 +502,14 @@ w32_close(int fd)
debug3("close - io:%p, type:%d, fd:%d, table_index:%d", pio, pio->type, fd,
pio->table_index);
- fd_table_clear(pio->table_index);
-
+
if (pio->type == SOCK_FD)
- return socketio_close(pio);
+ r = socketio_close(pio);
else
- switch (FILETYPE(pio)) {
- case FILE_TYPE_CHAR:
- return termio_close(pio);
- default:
- return fileio_close(pio);
- }
+ r = fileio_close(pio);
+
+ fd_table_clear(fd);
+ return r;
}
static int
@@ -798,7 +805,7 @@ w32_dup(int oldfd)
memset(pio, 0, sizeof(struct w32_io));
pio->handle = target;
- pio->type = NONSOCK_FD;
+ pio->type = fd_table.w32_ios[oldfd]->type;
fd_table_set(pio, min_index);
return min_index;
}
@@ -866,3 +873,131 @@ w32_fsync(int fd)
CHECK_FD(fd);
return FlushFileBuffers(w32_fd_to_handle(fd));
}
+
+
+/*
+* spawn a child process
+* - specified by cmd with agruments argv
+* - with std handles set to in, out, err
+* - flags are passed to CreateProcess call
+*
+* cmd will be internally decoarated with a set of '"'
+* to account for any spaces within the commandline
+* this decoration is done only when additional arguments are passed in argv
+*/
+int
+spawn_child(char* cmd, char** argv, int in, int out, int err, DWORD flags)
+{
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+ BOOL b;
+ char *cmdline, *t, **t1;
+ DWORD cmdline_len = 0;
+ wchar_t * cmdline_utf16;
+ int add_module_path = 0, ret = -1;
+
+ /* should module path be added */
+ do {
+ if (!cmd)
+ break;
+ t = cmd;
+ if (*t == '\"')
+ t++;
+ if (t[0] == '\0' || t[0] == '\\' || t[0] == '.' || t[1] == ':')
+ break;
+ add_module_path = 1;
+ } while (0);
+
+ /* compute total cmdline len*/
+ if (add_module_path)
+ cmdline_len += strlen(w32_programdir()) + 1 + strlen(cmd) + 1 + 2;
+ else
+ cmdline_len += strlen(cmd) + 1 + 2;
+
+ if (argv) {
+ t1 = argv;
+ while (*t1)
+ cmdline_len += strlen(*t1++) + 1 + 2;
+ }
+
+ if ((cmdline = malloc(cmdline_len)) == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+
+ /* add current module path to start if needed */
+ t = cmdline;
+ if (argv && argv[0])
+ *t++ = '\"';
+ if (add_module_path) {
+ memcpy(t, w32_programdir(), strlen(w32_programdir()));
+ t += strlen(w32_programdir());
+ *t++ = '\\';
+ }
+
+ memcpy(t, cmd, strlen(cmd));
+ t += strlen(cmd);
+
+ if (argv && argv[0])
+ *t++ = '\"';
+
+ if (argv) {
+ t1 = argv;
+ while (*t1) {
+ *t++ = ' ';
+ *t++ = '\"';
+ memcpy(t, *t1, strlen(*t1));
+ t += strlen(*t1);
+ *t++ = '\"';
+ t1++;
+ }
+ }
+
+ *t = '\0';
+
+ if ((cmdline_utf16 = utf8_to_utf16(cmdline)) == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+
+ memset(&si, 0, sizeof(STARTUPINFOW));
+ si.cb = sizeof(STARTUPINFOW);
+ si.hStdInput = w32_fd_to_handle(in);
+ si.hStdOutput = w32_fd_to_handle(out);
+ si.hStdError = w32_fd_to_handle(err);
+ si.dwFlags = STARTF_USESTDHANDLES;
+
+ debug3("spawning %ls", cmdline_utf16);
+ if (fd_table.w32_ios[in]->type != NONSOCK_SYNC_FD)
+ _putenv_s(SSH_ASYNC_STDIN, "1");
+ if (fd_table.w32_ios[out]->type != NONSOCK_SYNC_FD)
+ _putenv_s(SSH_ASYNC_STDOUT, "1");
+ if (fd_table.w32_ios[err]->type != NONSOCK_SYNC_FD)
+ _putenv_s(SSH_ASYNC_STDERR, "1");
+ b = CreateProcessW(NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
+ _putenv_s(SSH_ASYNC_STDIN, "");
+ _putenv_s(SSH_ASYNC_STDOUT, "");
+ _putenv_s(SSH_ASYNC_STDERR, "");
+
+ if (b) {
+ if (register_child(pi.hProcess, pi.dwProcessId) == -1) {
+ TerminateProcess(pi.hProcess, 0);
+ CloseHandle(pi.hProcess);
+ goto cleanup;
+ }
+ CloseHandle(pi.hThread);
+ }
+ else {
+ errno = GetLastError();
+ goto cleanup;
+ }
+
+ ret = pi.dwProcessId;
+cleanup:
+ if (cmdline)
+ free(cmdline);
+ if (cmdline_utf16)
+ free(cmdline_utf16);
+
+ return ret;
+}
diff --git a/contrib/win32/win32compat/w32fd.h b/contrib/win32/win32compat/w32fd.h
index 0387a00f4..22f235927 100644
--- a/contrib/win32/win32compat/w32fd.h
+++ b/contrib/win32/win32compat/w32fd.h
@@ -39,7 +39,22 @@ enum w32_io_type {
UNKNOWN_FD = 0,
SOCK_FD = 1, /*maps a socket fd*/
NONSOCK_FD = 2, /*maps a file fd, pipe fd or a tty fd*/
- STD_IO_FD = 5 /*maps a std fd - ex. STDIN_FILE*/
+ /*
+ * maps a NONSOCK_FD that doesnt support async or overlapped io
+ * these are typically used for stdio on ssh client side
+ * executables (ssh, sftp and scp).
+ * Ex. ssh ... > output.txt
+ * In the above case, stdout passed to ssh.exe is a handle to
+ * output.txt that is opened in non-overlapped mode
+ * Ex. sample.exe | ssh ...
+ * In the above case, stdin passed to ssh.exe is a handle to
+ * a pipe opened in non-overlapped mode
+ * Ex. in Powershell
+ * $o = ssh ...
+ * In the above case, stdout passed to ssh.exe is a handle to
+ * a pipe opened in non-overlapped mode
+ */
+ NONSOCK_SYNC_FD = 3
};
enum w32_io_sock_state {
@@ -51,7 +66,7 @@ enum w32_io_sock_state {
};
/*
-* This structure encapsulates the state info needed to map a File Descriptor
+* This structure encapsulates the I/O state info needed to map a File Descriptor
* to Win32 Handle
*/
struct w32_io {
@@ -94,7 +109,8 @@ struct w32_io {
}internal;
};
-#define WINHANDLE(pio) (((pio)->type == STD_IO_FD)? GetStdHandle((pio)->std_handle):(pio)->handle)
+#define IS_STDIO(pio) ((pio)->table_index <= 2)
+#define WINHANDLE(pio) (IS_STDIO(pio)? GetStdHandle((pio)->std_handle):(pio)->handle)
#define FILETYPE(pio) (GetFileType(WINHANDLE(pio)))
extern HANDLE main_thread;
@@ -102,7 +118,7 @@ BOOL w32_io_is_blocking(struct w32_io*);
BOOL w32_io_is_io_available(struct w32_io* pio, BOOL rd);
int wait_for_any_event(HANDLE* events, int num_events, DWORD milli_seconds);
-/*POSIX mimic'ing socket API*/
+/*POSIX mimic'ing socket API and socket helper API*/
int socketio_initialize();
int socketio_done();
BOOL socketio_is_io_available(struct w32_io* pio, BOOL rd);
@@ -122,7 +138,7 @@ int socketio_send(struct w32_io* pio, const void *buf, size_t len, int flags);
int socketio_shutdown(struct w32_io* pio, int how);
int socketio_close(struct w32_io* pio);
-/*POSIX mimic'ing file API*/
+/*POSIX mimic'ing file API and file helper API*/
BOOL fileio_is_io_available(struct w32_io* pio, BOOL rd);
void fileio_on_select(struct w32_io* pio, BOOL rd);
int fileio_close(struct w32_io* pio);
@@ -136,45 +152,3 @@ int fileio_fstat(struct w32_io* pio, struct _stat64 *buf);
int fileio_stat(const char *path, struct _stat64 *buf);
long fileio_lseek(struct w32_io* pio, long offset, int origin);
FILE* fileio_fdopen(struct w32_io* pio, const char *mode);
-
-/* terminal io specific versions */
-int termio_close(struct w32_io* pio);
-
-/*
-* open() flags and modes
-* all commented out macros are defined in fcntl.h
-* they are listed here so as to cross check any conflicts with macros explicitly
-* defined below.
-*/
-/*open access modes. only one of these can be specified*/
-/* #define O_RDONLY 0x0 */
-/* #define O_WRONLY 0x1 */
-/* #define O_RDWR 0x2 */
-/* open file creation and file status flags can be bitwise-or'd*/
-/* #define O_APPEND 0x8 /*file is opened in append mode*/
-#ifndef O_NONBLOCK
-#define O_NONBLOCK 0x0004 /*io operations wont block*/
-#endif
-/* #define O_CREAT 0x100 /*If the file does not exist it will be created*/
-/*
-* If the file exists and is a regular file, and the file is successfully
-* opened O_RDWR or O_WRONLY, its length shall be truncated to 0, and the mode
-* and owner shall be unchanged
-*/
-/* #define O_TRUNC 0x200 */
-/* If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
-/* #define O_EXCL 0x400 */
-/* #define O_BINARY 0x8000 //Gives raw data (while O_TEXT normalises line endings */
-/* open modes */
-#ifndef S_IRUSR
-#define S_IRUSR 00400 /* user has read permission */
-#endif /* ! S_IRUSR */
-#ifndef S_IWUSR
-#define S_IWUSR 00200 /* user has write permission */
-#endif
-#ifndef S_IRGRP
-#define S_IRGRP 00040 /* group has read permission */
-#endif
-#ifndef S_IROTH
-#define S_IROTH 00004 /* others have read permission */
-#endif
\ No newline at end of file
diff --git a/contrib/win32/win32compat/wmain_common.c b/contrib/win32/win32compat/wmain_common.c
index 3d09ceeac..fb6ca8f1e 100644
--- a/contrib/win32/win32compat/wmain_common.c
+++ b/contrib/win32/win32compat/wmain_common.c
@@ -52,8 +52,9 @@ wmain(int argc, wchar_t **wargv) {
if (getenv("SSH_AUTH_SOCK") == NULL)
_putenv("SSH_AUTH_SOCK=ssh-agent");
- w32posix_initialize();
- r = main(argc, argv);
- w32posix_done();
- return r;
+ w32posix_initialize();
+
+ r = main(argc, argv);
+ w32posix_done();
+ return r;
}
diff --git a/regress/pesterTests/SSH.Tests.ps1 b/regress/pesterTests/SSH.Tests.ps1
index 58cb7a6ee..74223bca5 100644
--- a/regress/pesterTests/SSH.Tests.ps1
+++ b/regress/pesterTests/SSH.Tests.ps1
@@ -1,14 +1,30 @@
-#covered -i -q -v -l -c -C
+#todo: -i -q -v -l -c -C
#todo: -S -F -V -e
-Describe "Tests for ssh command" -Tags "Scenario" {
+$tB = 1
+$tI = 0
+
+Describe "ssh client tests" -Tags "CI" {
BeforeAll {
- $fileName = "test.txt"
- $filePath = Join-Path ${TestDrive} $fileName
+ if($OpenSSHTestInfo -eq $null)
+ {
+ Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment."
+ }
- [Machine] $client = [Machine]::new([MachineRole]::Client)
- [Machine] $server = [Machine]::new([MachineRole]::Server)
- $client.SetupClient($server)
- $server.SetupServer($client)
+ if(-not (Test-Path $OpenSSHTestInfo["TestDataPath"]))
+ {
+ $null = New-Item $OpenSSHTestInfo["TestDataPath"] -ItemType directory -Force -ErrorAction SilentlyContinue
+ }
+
+ $server = $OpenSSHTestInfo["Target"]
+ $port = $OpenSSHTestInfo["Port"]
+ $ssouser = $OpenSSHTestInfo["SSOUser"]
+ $sshCmdDefault = "ssh -p $port $($ssouser)@$($server)"
+
+ $testDir = Join-Path $OpenSSHTestInfo["TestDataPath"] "ssh"
+ if(-not (Test-Path $testDir))
+ {
+ $null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
+ }
$testData = @(
@{
@@ -55,12 +71,71 @@ Describe "Tests for ssh command" -Tags "Scenario" {
}
- AfterAll {
- $client.CleanupClient()
- $server.CleanupServer()
+ BeforeEach {
+ $tI++;
+ $tFile=Join-Path $testDir "$tB.$tI.txt"
+ }
+
+ Context "$tB - Basic Scenarios" {
+
+ BeforeAll {$tI=1}
+ AfterAll{$tB++}
+
+ <# these 2 tests dont work on AppVeyor that sniffs stderr channel
+ It "$tB.$tI - test version" {
+ iex "ssh -V 2> $tFile"
+ $tFile | Should Contain "OpenSSH_"
+ }
+
+ It "$tB.$tI - test help" {
+ iex "ssh -? 2> $tFile"
+ $tFile | Should Contain "usage: ssh"
+ }
+ #>
+
+ It "$tB.$tI - remote echo command" {
+ iex "$sshDefaultCmd echo 1234" | Should Be "1234"
+ }
}
- Context "Key is not secured in ssh-agent on server" {
+ Context "$tB - Redirection Scenarios" {
+
+ BeforeAll {$tI=1}
+ AfterAll{$tB++}
+
+ It "$tB.$tI - stdout to file" {
+ iex "$sshDefaultCmd powershell get-process > $tFile"
+ $tFile | Should Contain "ProcessName"
+ }
+
+ It "$tB.$tI - stdout to PS object" {
+ $o = iex "$sshDefaultCmd echo 1234"
+ $o | Should Be "1234"
+ }
+
+ <#It "$tB.$tI - stdin from PS object" {
+ #if input redirection doesn't work, this would hang
+ 0 | ssh -p $port $ssouser@$server pause
+ $true | Should Be $true
+ }#>
+ }
+
+ Context "$tB - cmdline parameters" {
+
+ BeforeAll {$tI=1}
+ AfterAll{$tB++}
+
+ It "$tB.$tI - verbose to file" {
+ $logFile = Join-Path $testDir "$tB.$tI.log.txt"
+ $o = ssh -p $port -v -E $logFile $ssouser@$server echo 1234
+ $o | Should Be "1234"
+ #TODO - checks below are very inefficient (time taking).
+ $logFile | Should Contain "OpenSSH_"
+ $logFile | Should Contain "Exit Status 0"
+ }
+
+ }
+ <#Context "Key is not secured in ssh-agent on server" {
BeforeAll {
$identifyFile = $client.clientPrivateKeyPaths[0]
Remove-Item -Path $filePath -Force -ea silentlycontinue
@@ -156,5 +231,5 @@ Describe "Tests for ssh command" -Tags "Scenario" {
#validate file content.
Get-Content $filePath | Should be $server.MachineName
}
- }
+ }#>
}
diff --git a/session.c b/session.c
index 523acf4fa..a689b27ad 100644
--- a/session.c
+++ b/session.c
@@ -559,6 +559,10 @@ int do_exec_windows(Session *s, const char *command, int pty) {
debug("Executing command: %s", exec_command);
UTF8_TO_UTF16_FATAL(exec_command_w, exec_command);
+ _putenv_s("SSH_ASYNC_STDIN", "1");
+ _putenv_s("SSH_ASYNC_STDOUT", "1");
+ _putenv_s("SSH_ASYNC_STDERR", "1");
+
/* in debug mode launch using sshd.exe user context */
if (debug_flag)
b = CreateProcessW(NULL, exec_command_w, NULL, NULL, TRUE,
@@ -569,6 +573,10 @@ int do_exec_windows(Session *s, const char *command, int pty) {
DETACHED_PROCESS , NULL, pw_dir_w,
&si, &pi);
+ _putenv_s("SSH_ASYNC_STDIN", "");
+ _putenv_s("SSH_ASYNC_STDOUT", "");
+ _putenv_s("SSH_ASYNC_STDERR", "");
+
if (!b)
fatal("ERROR. Cannot create process (%u).\n", GetLastError());
else if (pty) { /*attach to shell console */