222 lines
6.4 KiB
C
222 lines
6.4 KiB
C
/*
|
|
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
|
|
* Primitive shell-host to support parsing of cmd.exe input and async IO redirection
|
|
*
|
|
* Copyright (c) 2015 Microsoft Corp.
|
|
* All rights reserved
|
|
*
|
|
* Microsoft openssh win32 port
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <Windows.h>
|
|
#include <stdio.h>
|
|
#include <io.h>
|
|
|
|
#define MAX_CMD_LEN 512
|
|
|
|
HANDLE pipe_in;
|
|
HANDLE pipe_out;
|
|
HANDLE pipe_err;
|
|
HANDLE child_pipe_read = INVALID_HANDLE_VALUE;
|
|
HANDLE child_pipe_write = INVALID_HANDLE_VALUE;
|
|
BOOL istty = TRUE; //TODO - set this to FALSE
|
|
HANDLE child = INVALID_HANDLE_VALUE, monitor_thread = INVALID_HANDLE_VALUE;
|
|
DWORD in_cmd_len = 0;
|
|
char in_cmd[MAX_CMD_LEN];
|
|
|
|
DWORD WINAPI MonitorChild(
|
|
_In_ LPVOID lpParameter
|
|
) {
|
|
WaitForSingleObject(child, INFINITE);
|
|
CloseHandle(pipe_in);
|
|
//printf("XXXX CHILD PROCESS DEAD XXXXX");
|
|
return 0;
|
|
}
|
|
|
|
#define GOTO_CLEANUP_ON_FALSE(exp) do { \
|
|
ret = (exp); \
|
|
if (ret == FALSE) \
|
|
goto cleanup; \
|
|
} while(0) \
|
|
|
|
#define GOTO_CLEANUP_ON_ERR(exp) do { \
|
|
if ((exp) != 0) \
|
|
goto cleanup; \
|
|
} while(0) \
|
|
|
|
int wmain(int ac, wchar_t **av) {
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
wchar_t cmd[MAX_PATH];
|
|
SECURITY_ATTRIBUTES sa;
|
|
BOOL ret;
|
|
|
|
pipe_in = GetStdHandle(STD_INPUT_HANDLE);
|
|
pipe_out = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
pipe_err = GetStdHandle(STD_ERROR_HANDLE);
|
|
|
|
/* copy pipe handles passed through std io*/
|
|
if ((pipe_in == INVALID_HANDLE_VALUE)
|
|
|| (pipe_out == INVALID_HANDLE_VALUE)
|
|
|| (pipe_err == INVALID_HANDLE_VALUE))
|
|
return -1;
|
|
|
|
memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
|
|
sa.bInheritHandle = TRUE;
|
|
if (!CreatePipe(&child_pipe_read, &child_pipe_write, &sa, 128))
|
|
return -1;
|
|
|
|
/* A console is attached if a tty is requested */
|
|
if (!AllocConsole())
|
|
istty = TRUE;
|
|
|
|
memset(&si, 0, sizeof(STARTUPINFO));
|
|
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
|
|
|
|
si.cb = sizeof(STARTUPINFO);
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
si.hStdInput = child_pipe_read;
|
|
si.hStdOutput = pipe_out;
|
|
si.hStdError = pipe_err;
|
|
|
|
/* disable inheritance on child_pipe_write and pipe_in*/
|
|
GOTO_CLEANUP_ON_FALSE(SetHandleInformation(pipe_in, HANDLE_FLAG_INHERIT, 0));
|
|
GOTO_CLEANUP_ON_FALSE(SetHandleInformation(child_pipe_write, HANDLE_FLAG_INHERIT, 0));
|
|
|
|
/* create job to hold all child processes */
|
|
{
|
|
/* TODO - this does not work as expected*/
|
|
HANDLE job = CreateJobObject(NULL, NULL);
|
|
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info;
|
|
memset(&job_info, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
|
job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
|
if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info)))
|
|
return -1;
|
|
CloseHandle(job);
|
|
}
|
|
|
|
/*TODO - pick this up from system32*/
|
|
cmd[0] = L'\0';
|
|
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L"cmd.exe"));
|
|
ac--;
|
|
av++;
|
|
if (ac)
|
|
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L" /c"));
|
|
while (ac) {
|
|
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L" "));
|
|
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, *av));
|
|
ac--;
|
|
av++;
|
|
}
|
|
|
|
GOTO_CLEANUP_ON_FALSE(CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi));
|
|
|
|
/* close unwanted handles*/
|
|
CloseHandle(child_pipe_read);
|
|
child_pipe_read = INVALID_HANDLE_VALUE;
|
|
|
|
child = pi.hProcess;
|
|
/* monitor child exist */
|
|
monitor_thread = CreateThread(NULL, 0, MonitorChild, NULL, 0, NULL);
|
|
if (monitor_thread == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
|
|
/* disable Ctrl+C hander in this process*/
|
|
SetConsoleCtrlHandler(NULL, TRUE);
|
|
|
|
/* process data from pipe_in and route appropriately */
|
|
while (1) {
|
|
char buf[128];
|
|
DWORD rd = 0, wr = 0, i = 0;
|
|
GOTO_CLEANUP_ON_FALSE(ReadFile(pipe_in, buf, 128, &rd, NULL));
|
|
|
|
if (!istty) { /* no tty, juet send it accross */
|
|
GOTO_CLEANUP_ON_FALSE(WriteFile(child_pipe_write, buf, rd, &wr, NULL));
|
|
continue;
|
|
}
|
|
|
|
while (i < rd) {
|
|
/* TODO - handle any pty data*/
|
|
|
|
/* skip them for now*/
|
|
if ((rd - i >= 3) && (buf[i] == '\033') && (buf[i + 1] == '[')
|
|
&& (buf[i + 2] >= 'A') && (buf[i + 2] <= 'D')) {
|
|
i += 3;
|
|
continue;
|
|
}
|
|
|
|
// Ctrl +C
|
|
if (buf[i] == '\003') {
|
|
GOTO_CLEANUP_ON_FALSE(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0));
|
|
in_cmd_len = 0;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// for backspace, we need to send space and another backspace for visual erase
|
|
if (buf[i] == '\b') {
|
|
if (in_cmd_len > 0) {
|
|
GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, "\b \b", 3, &wr, NULL));
|
|
in_cmd_len--;
|
|
}
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
//for CR and LF
|
|
if ((buf[i] == '\r') || (buf[i] == '\n')) {
|
|
|
|
/* TODO - do a much accurate mapping */
|
|
buf[i] = '\n';
|
|
GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, buf + i, 1, &wr, NULL));
|
|
in_cmd[in_cmd_len] = buf[i];
|
|
in_cmd_len++;
|
|
GOTO_CLEANUP_ON_FALSE(WriteFile(child_pipe_write, in_cmd, in_cmd_len, &wr, NULL));
|
|
in_cmd_len = 0;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
|
|
GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, buf + i, 1, &wr, NULL));
|
|
in_cmd[in_cmd_len] = buf[i];
|
|
in_cmd_len++;
|
|
if (in_cmd_len == MAX_CMD_LEN - 1) {
|
|
GOTO_CLEANUP_ON_FALSE(WriteFile(child_pipe_write, in_cmd, in_cmd_len, &wr, NULL));
|
|
in_cmd_len = 0;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (child != INVALID_HANDLE_VALUE)
|
|
TerminateProcess(child, 0);
|
|
if (monitor_thread != INVALID_HANDLE_VALUE)
|
|
WaitForSingleObject(monitor_thread, INFINITE);
|
|
return 0;
|
|
}
|
|
|