Skip to content
Snippets Groups Projects
Commit 639be7f8 authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Add Shell support for Windows

Requires Windows 10 or higher.  This changes the default SyncTERM
builds to require Windows 10.  To build for other versions, you
need to pass WIN32, WINVER, WIN32_IE, and NTDDI to build script.

These need to be whatever they need to be for old stuff to work.
parent 8e14b84e
No related branches found
No related tags found
No related merge requests found
Pipeline #7619 failed
......@@ -246,7 +246,12 @@ ifndef os
endif
os := $(shell echo $(os) | tr '[A-Z]' '[a-z]' | tr ' ' '_')
ifndef win
ifdef win
WINNT ?= 0x0501
WINVER ?= 0x0501
WIN32_IE ?= 0x0500
NTDDI ?= 0x0500
else
CFLAGS += -DPREFER_POLL
machine_uname := $(shell if uname -m | egrep -v "(i[3456789]*|x)86" > /dev/null; then uname -m | tr "[A-Z]" "[a-z]" | tr " " "_" ; fi)
machine_uname := $(shell if uname -m | egrep "64" > /dev/null; then uname -m | tr "[A-Z]" "[a-z]" | tr " " "_" ; else echo $(machine_uname) ; fi)
......@@ -445,7 +450,7 @@ else
#CFLAGS += -D_POSIX_PTHREAD_SEMANTICS
else
ifdef win # Windows
CFLAGS += -U__STRICT_ANSI__ -D_WIN32 -D_WIN32_WINNT=0x0501 -DWINVER=0x0501 -D_WIN32_IE=0x0500
CFLAGS += -U__STRICT_ANSI__ -D_WIN32 -D_WIN32_WINNT=${WINNT} -DWINVER=${WINVER} -D_WIN32_IE=${WIN32_IE} -DNTDDI_VERSION=${NTDDI}
MT_CFLAGS += -D_WIN32
else
ifeq ($(os),emscripten)
......
SRC_ROOT := ..
WINVER ?= 0xA00
WINNT ?= 0xA00
WIN32_IE ?= 0xA00
NTDDI ?= 0x0A000006
include ${SRC_ROOT}/build/Common.gmake
ifdef NEED_BITMAP
......@@ -46,6 +50,7 @@ ifdef win
CFLAGS += -DWITH_JPEG_XL -DWITH_STATIC_JXL
CFLAGS += -DWITH_JPEG_XL_THREADS
OBJS += $(MTOBJODIR)$(DIRSEP)libjxl$(OFILE)
OBJS += $(MTOBJODIR)$(DIRSEP)conn_conpty$(OFILE)
else
ifeq ($(shell pkg-config libjxl --exists && echo YES), YES)
CFLAGS += $(shell pkg-config libjxl --cflags)
......
......@@ -44,6 +44,9 @@
#ifdef __unix__
#include "conn_pty.h"
#endif
#ifdef _WIN32
#include "conn_conpty.h"
#endif
#include "conn_telnet.h"
#ifdef _MSC_VER
......@@ -417,6 +420,12 @@ conn_connect(struct bbslist *bbs)
conn_api.connect = pty_connect;
conn_api.close = pty_close;
break;
#endif
#ifdef HAS_CONPTY
case CONN_TYPE_SHELL:
conn_api.connect = conpty_connect;
conn_api.close = conpty_close;
break;
#endif
default:
sprintf(str, "%s connections not supported.", conn_types[bbs->conn_type]);
......
#if NTDDI_VERSION >= 0x0A000006
#define WIN32_LEAN_AND_MEAN
#include <stdatomic.h>
#include <windows.h>
#include <wincon.h>
#include "bbslist.h"
#include "conn.h"
#include "uifcinit.h"
#include "window.h"
HANDLE inputRead, inputWrite, outputRead, outputWrite;
PROCESS_INFORMATION pi;
HPCON cpty;
static atomic_bool terminate;
static void
conpty_input_thread(void *args)
{
DWORD rd;
int buffered;
size_t buffer;
int i;
DWORD ec;
SetThreadName("PTY Input");
conn_api.input_thread_running = 1;
while (!terminate && !conn_api.terminate) {
if (GetExitCodeProcess(pi.hProcess, &ec)) {
if (ec != STILL_ACTIVE)
break;
}
else {
break;
}
if (!ReadFile(outputRead, conn_api.rd_buf, conn_api.rd_buf_size, &rd, NULL)) {
break;
}
buffered = 0;
while (!terminate && !conn_api.terminate && buffered < rd) {
pthread_mutex_lock(&(conn_inbuf.mutex));
buffer = conn_buf_wait_free(&conn_inbuf, rd - buffered, 100);
buffered += conn_buf_put(&conn_inbuf, conn_api.rd_buf + buffered, buffer);
pthread_mutex_unlock(&(conn_inbuf.mutex));
}
}
terminate = true;
conn_api.input_thread_running = 2;
}
static void
conpty_output_thread(void *args)
{
int wr;
DWORD ret;
int sent;
DWORD ec;
SetThreadName("PTY Output");
conn_api.output_thread_running = 1;
while (!terminate && !conn_api.terminate) {
if (GetExitCodeProcess(pi.hProcess, &ec)) {
if (ec != STILL_ACTIVE)
break;
}
else {
break;
}
pthread_mutex_lock(&(conn_outbuf.mutex));
ret = 0;
wr = conn_buf_wait_bytes(&conn_outbuf, 1, 100);
if (wr) {
wr = conn_buf_get(&conn_outbuf, conn_api.wr_buf, conn_api.wr_buf_size);
pthread_mutex_unlock(&(conn_outbuf.mutex));
sent = 0;
while (!terminate && !conn_api.terminate && sent < wr) {
if (!WriteFile(inputWrite, conn_api.wr_buf + sent, wr - sent, &ret, NULL)) {
terminate = true;
break;
}
sent += ret;
}
}
else {
pthread_mutex_unlock(&(conn_outbuf.mutex));
}
}
conn_api.output_thread_running = 2;
}
int conpty_connect(struct bbslist *bbs)
{
HANDLE heap = GetProcessHeap();
int w, h;
get_term_win_size(&w, &h, NULL, NULL, &bbs->nostatus);
COORD size = {
.X = w,
.Y = h
};
STARTUPINFOEXA si = {
.StartupInfo = {
.cb = sizeof(STARTUPINFOEXA)
}
};
size_t sz;
// "Note This initial call will return an error by design. This is expected behavior."
!InitializeProcThreadAttributeList(NULL, 1, 0, &sz);
si.lpAttributeList = HeapAlloc(heap, 0, sz);
if (si.lpAttributeList == NULL) {
uifcmsg("TODO", "HeapAlloc Failed");
return -1;
}
char *cmd = bbs->addr;
if (cmd[0] == 0)
cmd = getenv("ComSpec");
if (cmd == NULL) {
uifcmsg("TODO", "cmd Failed");
return -1;
}
if (!CreatePipe(&inputRead, &inputWrite, NULL, 0)) {
uifcmsg("TODO", "CreatePipe (input) Failed");
return -1;
}
if (!CreatePipe(&outputRead, &outputWrite, NULL, 0)) {
CloseHandle(inputRead);
CloseHandle(inputWrite);
HeapFree(heap, 0, si.lpAttributeList);
uifcmsg("TODO", "CreatePipe (output) Failed");
return -1;
}
if (FAILED(CreatePseudoConsole(size, inputRead, outputWrite, 0, &cpty))) {
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
HeapFree(heap, 0, si.lpAttributeList);
uifcmsg("TODO", "CreatePseudoConsole Failed");
return -1;
}
if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &sz)) {
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
HeapFree(heap, 0, si.lpAttributeList);
uifcmsg("TODO", "InitializeProcThreadAttributeList2 Failed");
return -1;
}
if (!UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, cpty, sizeof(cpty), NULL, NULL)) {
DeleteProcThreadAttributeList(si.lpAttributeList);
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
HeapFree(heap, 0, si.lpAttributeList);
uifcmsg("TODO", "UpdateProcThreadAttribute Failed");
return -1;
}
if (!CreateProcessA(NULL, cmd, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi)) {
DeleteProcThreadAttributeList(si.lpAttributeList);
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
HeapFree(heap, 0, si.lpAttributeList);
uifcmsg("TODO", "CreateProcessA Failed");
return -1;
}
DeleteProcThreadAttributeList(si.lpAttributeList);
HeapFree(heap, 0, si.lpAttributeList);
if (!create_conn_buf(&conn_inbuf, BUFFER_SIZE)) {
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
uifcmsg("TODO", "create_conn_buf (input) Failed");
return -1;
}
if (!create_conn_buf(&conn_outbuf, BUFFER_SIZE)) {
destroy_conn_buf(&conn_inbuf);
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
uifcmsg("TODO", "create_conn_buf (output) Failed");
return -1;
}
if (!(conn_api.rd_buf = (unsigned char *)malloc(BUFFER_SIZE))) {
destroy_conn_buf(&conn_inbuf);
destroy_conn_buf(&conn_outbuf);
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
uifcmsg("TODO", "malloc (input) Failed");
return -1;
}
conn_api.rd_buf_size = BUFFER_SIZE;
if (!(conn_api.wr_buf = (unsigned char *)malloc(BUFFER_SIZE))) {
free(conn_api.rd_buf);
destroy_conn_buf(&conn_inbuf);
destroy_conn_buf(&conn_outbuf);
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
uifcmsg("TODO", "malloc (output) Failed");
return -1;
}
conn_api.wr_buf_size = BUFFER_SIZE;
_beginthread(conpty_output_thread, 0, NULL);
_beginthread(conpty_input_thread, 0, NULL);
return 0;
}
int
conpty_close(void)
{
char garbage[1024];
DWORD ret;
conn_api.terminate = 1;
terminate = true;
TerminateProcess(pi.hProcess, 0);
WaitForSingleObject(pi.hProcess, 1000);
ClosePseudoConsole(cpty);
WriteFile(outputWrite, "Die", 3, &ret, NULL);
while (conn_api.input_thread_running == 1 || conn_api.output_thread_running == 1) {
conn_recv_upto(garbage, sizeof(garbage), 0);
SLEEP(1);
}
destroy_conn_buf(&conn_inbuf);
destroy_conn_buf(&conn_outbuf);
FREE_AND_NULL(conn_api.rd_buf);
FREE_AND_NULL(conn_api.wr_buf);
CloseHandle(inputRead);
CloseHandle(inputWrite);
CloseHandle(outputRead);
CloseHandle(outputWrite);
}
#endif
#ifndef CONN_CONPTY_H
#define CONN_CONPTY_H
#ifdef _WIN32
#if NTDDI_VERSION >= 0x0A000006
#define HAS_CONPTY
int conpty_connect(struct bbslist *bbs);
int conpty_close(void);
#endif
#endif
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment