Feature: Add terminal support for macOS client with shared PTYHandler
This commit is contained in:
270
common/PTYHandler.h
Normal file
270
common/PTYHandler.h
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/**
|
||||||
|
* PTYHandler.h - Unix Pseudo Terminal Handler
|
||||||
|
*
|
||||||
|
* This file provides pseudo terminal (PTY) functionality for remote shell access.
|
||||||
|
*
|
||||||
|
* PLATFORM SUPPORT:
|
||||||
|
* - Linux: Supported
|
||||||
|
* - macOS: Supported
|
||||||
|
* - Windows: NOT SUPPORTED (Windows uses different terminal APIs)
|
||||||
|
*
|
||||||
|
* USAGE:
|
||||||
|
* #include "common/PTYHandler.h"
|
||||||
|
*
|
||||||
|
* PTYHandler* handler = new PTYHandler(clientObject);
|
||||||
|
* clientObject->setManagerCallBack(handler, ...);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
#error "PTYHandler.h is not supported on Windows. Use Windows ConPTY or other APIs instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Platform-specific includes
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <util.h> // macOS: openpty()
|
||||||
|
#else
|
||||||
|
#include <pty.h> // Linux: openpty()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Common Unix includes
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "commands.h"
|
||||||
|
#include "../client/IOCPClient.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PTYHandler - Pseudo Terminal Handler
|
||||||
|
*
|
||||||
|
* Manages a pseudo terminal for remote shell access.
|
||||||
|
* Inherits from IOCPManager to integrate with the IOCP client framework.
|
||||||
|
*/
|
||||||
|
class PTYHandler : public IOCPManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Non-copyable, non-movable (owns system resources)
|
||||||
|
PTYHandler(const PTYHandler&) = delete;
|
||||||
|
PTYHandler& operator=(const PTYHandler&) = delete;
|
||||||
|
PTYHandler(PTYHandler&&) = delete;
|
||||||
|
PTYHandler& operator=(PTYHandler&&) = delete;
|
||||||
|
|
||||||
|
PTYHandler(IOCPClient* client) : m_client(client), m_running(false), m_master_fd(-1), m_slave_fd(-1), m_child_pid(-1)
|
||||||
|
{
|
||||||
|
if (!client) {
|
||||||
|
throw std::invalid_argument("IOCPClient pointer cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pseudo terminal pair
|
||||||
|
if (openpty(&m_master_fd, &m_slave_fd, nullptr, nullptr, nullptr) == -1) {
|
||||||
|
throw std::runtime_error("Failed to create pseudo terminal");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set master fd to non-blocking mode
|
||||||
|
int flags = fcntl(m_master_fd, F_GETFL, 0);
|
||||||
|
fcntl(m_master_fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
// Start shell process
|
||||||
|
startShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
~PTYHandler()
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
if (m_readThread.joinable()) {
|
||||||
|
m_readThread.join();
|
||||||
|
}
|
||||||
|
if (m_master_fd >= 0) {
|
||||||
|
close(m_master_fd);
|
||||||
|
}
|
||||||
|
if (m_slave_fd >= 0) {
|
||||||
|
close(m_slave_fd);
|
||||||
|
}
|
||||||
|
if (m_child_pid > 0) {
|
||||||
|
// Check if child is still running before killing
|
||||||
|
int status;
|
||||||
|
pid_t result = waitpid(m_child_pid, &status, WNOHANG);
|
||||||
|
if (result == 0) {
|
||||||
|
// Child still running, terminate it
|
||||||
|
kill(m_child_pid, SIGTERM);
|
||||||
|
waitpid(m_child_pid, nullptr, 0);
|
||||||
|
}
|
||||||
|
// If result == m_child_pid, child already exited and was reaped
|
||||||
|
// If result == -1, child was already reaped elsewhere
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the PTY read thread
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
bool expected = false;
|
||||||
|
if (!m_running.compare_exchange_strong(expected, true)) return;
|
||||||
|
m_readThread = std::thread(&PTYHandler::readFromPTY, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle incoming data from server
|
||||||
|
virtual VOID OnReceive(PBYTE data, ULONG size) override
|
||||||
|
{
|
||||||
|
if (size && data[0] == COMMAND_NEXT) {
|
||||||
|
Start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle terminal resize command
|
||||||
|
if (size >= 5 && data[0] == CMD_TERMINAL_RESIZE) {
|
||||||
|
short cols, rows;
|
||||||
|
memcpy(&cols, data + 1, sizeof(short));
|
||||||
|
memcpy(&rows, data + 3, sizeof(short));
|
||||||
|
SetWindowSize(cols, rows);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to PTY
|
||||||
|
if (size > 0) {
|
||||||
|
ssize_t total = 0;
|
||||||
|
while (total < (ssize_t)size) {
|
||||||
|
ssize_t written = write(m_master_fd, (char*)data + total, size - total);
|
||||||
|
if (written == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
total += written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set terminal window size
|
||||||
|
void SetWindowSize(int cols, int rows)
|
||||||
|
{
|
||||||
|
struct winsize ws;
|
||||||
|
ws.ws_col = cols;
|
||||||
|
ws.ws_row = rows;
|
||||||
|
ws.ws_xpixel = 0;
|
||||||
|
ws.ws_ypixel = 0;
|
||||||
|
|
||||||
|
if (ioctl(m_master_fd, TIOCSWINSZ, &ws) == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send SIGWINCH to child process to notify window size change
|
||||||
|
if (m_child_pid > 0) {
|
||||||
|
kill(m_child_pid, SIGWINCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_master_fd;
|
||||||
|
int m_slave_fd;
|
||||||
|
IOCPClient* m_client;
|
||||||
|
std::thread m_readThread;
|
||||||
|
std::atomic<bool> m_running;
|
||||||
|
pid_t m_child_pid;
|
||||||
|
|
||||||
|
void startShell()
|
||||||
|
{
|
||||||
|
m_child_pid = fork();
|
||||||
|
if (m_child_pid == -1) {
|
||||||
|
close(m_master_fd);
|
||||||
|
close(m_slave_fd);
|
||||||
|
throw std::runtime_error("Failed to fork shell process");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_child_pid == 0) {
|
||||||
|
// Child process
|
||||||
|
setsid(); // Create new session, become session leader
|
||||||
|
|
||||||
|
// Set slave PTY as controlling terminal (required for Ctrl+C to work)
|
||||||
|
// This must be done after setsid() and before dup2()
|
||||||
|
ioctl(m_slave_fd, TIOCSCTTY, 0);
|
||||||
|
|
||||||
|
// Redirect stdin/stdout/stderr to slave PTY
|
||||||
|
dup2(m_slave_fd, STDIN_FILENO);
|
||||||
|
dup2(m_slave_fd, STDOUT_FILENO);
|
||||||
|
dup2(m_slave_fd, STDERR_FILENO);
|
||||||
|
close(m_master_fd);
|
||||||
|
close(m_slave_fd);
|
||||||
|
|
||||||
|
// Set terminal environment for xterm.js compatibility
|
||||||
|
setenv("TERM", "xterm-256color", 1);
|
||||||
|
setenv("COLORTERM", "truecolor", 1);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// macOS locale settings
|
||||||
|
setenv("LANG", "en_US.UTF-8", 1);
|
||||||
|
setenv("LC_ALL", "en_US.UTF-8", 1);
|
||||||
|
|
||||||
|
// Try zsh first (macOS default), fallback to bash
|
||||||
|
if (access("/bin/zsh", X_OK) == 0) {
|
||||||
|
execl("/bin/zsh", "zsh", "-i", nullptr);
|
||||||
|
}
|
||||||
|
execl("/bin/bash", "bash", "-i", nullptr);
|
||||||
|
#else
|
||||||
|
// Linux locale settings (C.UTF-8 is most portable)
|
||||||
|
setenv("LANG", "C.UTF-8", 1);
|
||||||
|
setenv("LC_ALL", "C.UTF-8", 1);
|
||||||
|
|
||||||
|
// Start interactive bash
|
||||||
|
execl("/bin/bash", "bash", "-i", nullptr);
|
||||||
|
#endif
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void readFromPTY()
|
||||||
|
{
|
||||||
|
char buffer[4096];
|
||||||
|
while (m_running) {
|
||||||
|
// Check if child process has exited
|
||||||
|
int status;
|
||||||
|
pid_t result = waitpid(m_child_pid, &status, WNOHANG);
|
||||||
|
if (result == m_child_pid) {
|
||||||
|
// Shell exited, send close notification
|
||||||
|
if (m_client) {
|
||||||
|
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
||||||
|
m_client->Send2Server((char*)&closeToken, 1);
|
||||||
|
}
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t bytes_read = read(m_master_fd, buffer, sizeof(buffer) - 1);
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
if (m_client) {
|
||||||
|
m_client->Send2Server(buffer, bytes_read);
|
||||||
|
}
|
||||||
|
} else if (bytes_read == 0) {
|
||||||
|
// EOF - PTY closed
|
||||||
|
if (m_client) {
|
||||||
|
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
||||||
|
m_client->Send2Server((char*)&closeToken, 1);
|
||||||
|
}
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
} else if (bytes_read == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
usleep(10000); // 10ms
|
||||||
|
} else if (errno == EIO) {
|
||||||
|
// EIO typically means PTY slave closed (shell exited)
|
||||||
|
if (m_client) {
|
||||||
|
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
||||||
|
m_client->Send2Server((char*)&closeToken, 1);
|
||||||
|
}
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
184
linux/main.cpp
184
linux/main.cpp
@@ -14,7 +14,7 @@
|
|||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <pty.h>
|
#include "common/PTYHandler.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -338,187 +338,7 @@ struct RttEstimator {
|
|||||||
RttEstimator g_rttEstimator;
|
RttEstimator g_rttEstimator;
|
||||||
int g_heartbeatInterval = 5; // 默认心跳间隔(秒),可被服务端 CMD_MASTERSETTING 更新
|
int g_heartbeatInterval = 5; // 默认心跳间隔(秒),可被服务端 CMD_MASTERSETTING 更新
|
||||||
|
|
||||||
// 伪终端处理类:继承自IOCPManager.
|
// PTYHandler moved to common/PTYHandler.h (shared between Linux and macOS)
|
||||||
class PTYHandler : public IOCPManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PTYHandler(IOCPClient* client) : m_client(client), m_running(false)
|
|
||||||
{
|
|
||||||
if (!client) {
|
|
||||||
throw std::invalid_argument("IOCPClient pointer cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建伪终端
|
|
||||||
if (openpty(&m_master_fd, &m_slave_fd, nullptr, nullptr, nullptr) == -1) {
|
|
||||||
throw std::runtime_error("Failed to create pseudo terminal");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置伪终端为非阻塞模式
|
|
||||||
int flags = fcntl(m_master_fd, F_GETFL, 0);
|
|
||||||
fcntl(m_master_fd, F_SETFL, flags | O_NONBLOCK);
|
|
||||||
|
|
||||||
// 启动 Shell 进程
|
|
||||||
startShell();
|
|
||||||
}
|
|
||||||
|
|
||||||
~PTYHandler()
|
|
||||||
{
|
|
||||||
m_running = false;
|
|
||||||
if (m_readThread.joinable()) m_readThread.join();
|
|
||||||
close(m_master_fd);
|
|
||||||
close(m_slave_fd);
|
|
||||||
if (m_child_pid > 0) {
|
|
||||||
kill(m_child_pid, SIGTERM);
|
|
||||||
waitpid(m_child_pid, nullptr, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动读取线程
|
|
||||||
void Start()
|
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
if (!m_running.compare_exchange_strong(expected, true)) return;
|
|
||||||
m_readThread = std::thread(&PTYHandler::readFromPTY, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual VOID OnReceive(PBYTE data, ULONG size)
|
|
||||||
{
|
|
||||||
if (size && data[0] == COMMAND_NEXT) {
|
|
||||||
Start();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 处理终端尺寸调整命令
|
|
||||||
if (size >= 5 && data[0] == CMD_TERMINAL_RESIZE) {
|
|
||||||
int cols = *(short*)(data + 1);
|
|
||||||
int rows = *(short*)(data + 3);
|
|
||||||
SetWindowSize(cols, rows);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string s((char*)data, size);
|
|
||||||
Mprintf("%s", s.c_str());
|
|
||||||
if (size > 0) {
|
|
||||||
ssize_t total = 0;
|
|
||||||
while (total < (ssize_t)size) {
|
|
||||||
ssize_t written = write(m_master_fd, (char*)data + total, size - total);
|
|
||||||
if (written == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EINTR) continue;
|
|
||||||
Mprintf("OnReceive: write error %d\n", errno);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
total += written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置终端窗口尺寸
|
|
||||||
void SetWindowSize(int cols, int rows)
|
|
||||||
{
|
|
||||||
struct winsize ws;
|
|
||||||
ws.ws_col = cols;
|
|
||||||
ws.ws_row = rows;
|
|
||||||
ws.ws_xpixel = 0;
|
|
||||||
ws.ws_ypixel = 0;
|
|
||||||
|
|
||||||
if (ioctl(m_master_fd, TIOCSWINSZ, &ws) == -1) {
|
|
||||||
Mprintf("SetWindowSize: ioctl failed %d\n", errno);
|
|
||||||
} else {
|
|
||||||
// 发送 SIGWINCH 给子进程,通知其窗口大小已改变
|
|
||||||
if (m_child_pid > 0) {
|
|
||||||
kill(m_child_pid, SIGWINCH);
|
|
||||||
}
|
|
||||||
Mprintf("SetWindowSize: %dx%d\n", cols, rows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
int m_master_fd, m_slave_fd;
|
|
||||||
IOCPClient* m_client;
|
|
||||||
std::thread m_readThread;
|
|
||||||
std::atomic<bool> m_running;
|
|
||||||
pid_t m_child_pid;
|
|
||||||
|
|
||||||
void startShell()
|
|
||||||
{
|
|
||||||
m_child_pid = fork();
|
|
||||||
if (m_child_pid == -1) {
|
|
||||||
close(m_master_fd);
|
|
||||||
close(m_slave_fd);
|
|
||||||
throw std::runtime_error("Failed to fork shell process");
|
|
||||||
}
|
|
||||||
if (m_child_pid == 0) { // 子进程
|
|
||||||
setsid(); // 创建新的会话
|
|
||||||
dup2(m_slave_fd, STDIN_FILENO);
|
|
||||||
dup2(m_slave_fd, STDOUT_FILENO);
|
|
||||||
dup2(m_slave_fd, STDERR_FILENO);
|
|
||||||
close(m_master_fd);
|
|
||||||
close(m_slave_fd);
|
|
||||||
|
|
||||||
// 设置完整终端支持(xterm.js 终端仿真)
|
|
||||||
setenv("TERM", "xterm-256color", 1);
|
|
||||||
setenv("COLORTERM", "truecolor", 1);
|
|
||||||
// 使用 C.UTF-8 是最通用的 UTF-8 locale,几乎所有 Linux 都支持
|
|
||||||
setenv("LANG", "C.UTF-8", 1);
|
|
||||||
setenv("LC_ALL", "C.UTF-8", 1);
|
|
||||||
|
|
||||||
// 启动交互式 Bash
|
|
||||||
execl("/bin/bash", "bash", "-i", nullptr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void readFromPTY()
|
|
||||||
{
|
|
||||||
char buffer[4096];
|
|
||||||
while (m_running) {
|
|
||||||
// 检查子进程是否已退出
|
|
||||||
int status;
|
|
||||||
pid_t result = waitpid(m_child_pid, &status, WNOHANG);
|
|
||||||
if (result == m_child_pid) {
|
|
||||||
// Shell 已退出,发送关闭通知
|
|
||||||
Mprintf("readFromPTY: shell exited (status=%d)\n", WEXITSTATUS(status));
|
|
||||||
if (m_client) {
|
|
||||||
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
|
||||||
m_client->Send2Server((char*)&closeToken, 1);
|
|
||||||
}
|
|
||||||
m_running = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t bytes_read = read(m_master_fd, buffer, sizeof(buffer) - 1);
|
|
||||||
if (bytes_read > 0) {
|
|
||||||
if (m_client) {
|
|
||||||
buffer[bytes_read] = '\0';
|
|
||||||
Mprintf("%s", buffer);
|
|
||||||
m_client->Send2Server(buffer, bytes_read);
|
|
||||||
}
|
|
||||||
} else if (bytes_read == 0) {
|
|
||||||
// EOF - PTY 已关闭
|
|
||||||
Mprintf("readFromPTY: EOF (shell closed)\n");
|
|
||||||
if (m_client) {
|
|
||||||
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
|
||||||
m_client->Send2Server((char*)&closeToken, 1);
|
|
||||||
}
|
|
||||||
m_running = false;
|
|
||||||
break;
|
|
||||||
} else if (bytes_read == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
usleep(10000);
|
|
||||||
} else if (errno == EIO) {
|
|
||||||
// EIO 通常表示 PTY slave 已关闭(shell 退出)
|
|
||||||
Mprintf("readFromPTY: EIO (shell closed)\n");
|
|
||||||
if (m_client) {
|
|
||||||
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
|
||||||
m_client->Send2Server((char*)&closeToken, 1);
|
|
||||||
}
|
|
||||||
m_running = false;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
Mprintf("readFromPTY: read error %d\n", errno);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void* ShellworkingThread(void* param)
|
void* ShellworkingThread(void* param)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#import "ScreenHandler.h"
|
#import "ScreenHandler.h"
|
||||||
#import "InputHandler.h"
|
#import "InputHandler.h"
|
||||||
#import "SystemManager.h"
|
#import "SystemManager.h"
|
||||||
|
#import "common/PTYHandler.h"
|
||||||
|
|
||||||
// Global state
|
// Global state
|
||||||
static std::atomic<bool> g_running(true);
|
static std::atomic<bool> g_running(true);
|
||||||
@@ -615,6 +616,28 @@ struct RttEstimator {
|
|||||||
RttEstimator g_rttEstimator;
|
RttEstimator g_rttEstimator;
|
||||||
int g_heartbeatInterval = 5; // 心跳间隔(秒),默认 5 秒,后续可由服务端动态调整
|
int g_heartbeatInterval = 5; // 心跳间隔(秒),默认 5 秒,后续可由服务端动态调整
|
||||||
|
|
||||||
|
void* ShellworkingThread(void* param)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
|
||||||
|
void* clientAddr = ClientObject.get();
|
||||||
|
NSLog(@">>> Enter ShellworkingThread [%p]", clientAddr);
|
||||||
|
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
|
||||||
|
std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get()));
|
||||||
|
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
|
||||||
|
BYTE bToken = TOKEN_TERMINAL_START;
|
||||||
|
ClientObject->Send2Server((char*)&bToken, 1);
|
||||||
|
NSLog(@">>> ShellworkingThread [%p] Send: TOKEN_TERMINAL_START", clientAddr);
|
||||||
|
while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit)
|
||||||
|
Sleep(1000);
|
||||||
|
}
|
||||||
|
NSLog(@">>> Leave ShellworkingThread [%p]", clientAddr);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
NSLog(@"*** ShellworkingThread exception: %s ***", e.what());
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void* ScreenworkingThread(void* param)
|
void* ScreenworkingThread(void* param)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -651,6 +674,7 @@ int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
|
|||||||
g_bExit = S_CLIENT_EXIT;
|
g_bExit = S_CLIENT_EXIT;
|
||||||
g_running = false; // Stop main loop to prevent reconnection
|
g_running = false; // Stop main loop to prevent reconnection
|
||||||
} else if (szBuffer[0] == COMMAND_SHELL) {
|
} else if (szBuffer[0] == COMMAND_SHELL) {
|
||||||
|
std::thread(ShellworkingThread, nullptr).detach();
|
||||||
Mprintf("** [%p] Received 'SHELL' command ***\n", user);
|
Mprintf("** [%p] Received 'SHELL' command ***\n", user);
|
||||||
} else if (szBuffer[0] == COMMAND_SCREEN_SPY) {
|
} else if (szBuffer[0] == COMMAND_SCREEN_SPY) {
|
||||||
std::thread(ScreenworkingThread, nullptr).detach();
|
std::thread(ScreenworkingThread, nullptr).detach();
|
||||||
|
|||||||
Reference in New Issue
Block a user