use strings and forkpty

This commit is contained in:
2025-04-12 17:16:40 -05:00
parent e1b93ea51b
commit a3b3a9b6e8
3 changed files with 40 additions and 127 deletions

View File

@@ -53,6 +53,7 @@
modules/ImguiModule/src/*.cpp \ modules/ImguiModule/src/*.cpp \
$imgui/backends/imgui_impl_glfw.cpp \ $imgui/backends/imgui_impl_glfw.cpp \
$imgui/backends/imgui_impl_opengl3.cpp \ $imgui/backends/imgui_impl_opengl3.cpp \
$imgui/misc/cpp/*.cpp \
$imgui/*.cpp \ $imgui/*.cpp \
-DRENDERER_OPENGL \ -DRENDERER_OPENGL \
-DWINDOW_GLFW \ -DWINDOW_GLFW \
@@ -94,6 +95,7 @@
modules/ImguiModule/src/*.cpp \ modules/ImguiModule/src/*.cpp \
$imgui/backends/imgui_impl_glfw.cpp \ $imgui/backends/imgui_impl_glfw.cpp \
$imgui/backends/imgui_impl_opengl3.cpp \ $imgui/backends/imgui_impl_opengl3.cpp \
$imgui/misc/cpp/*.cpp \
$imgui/*.cpp \ $imgui/*.cpp \
-DRENDERER_OPENGL \ -DRENDERER_OPENGL \
-DWINDOW_GLFW \ -DWINDOW_GLFW \

View File

@@ -1,14 +1,8 @@
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
//#include <stdio.h> #include <pty.h>
#define __USE_BSD #include <fcntl.h>
#include <termios.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <string.h>
#include "Terminal.h" #include "Terminal.h"
#include "modules/ImguiModule/src/ImguiModule.h" #include "modules/ImguiModule/src/ImguiModule.h"
@@ -37,140 +31,57 @@ void Terminal::onLoad() {
ImGui::SetCurrentContext(im->getContext()); ImGui::SetCurrentContext(im->getContext());
master = posix_openpt(O_RDWR); child_pid = forkpty(&master, nullptr, nullptr, nullptr);
if (master < 0) {
std::cerr << "Error " << errno << " on posix_openpt()\n";
std::abort();
}
int returnCode = grantpt(master); if(!child_pid) {
if (returnCode != 0) {
std::cerr << "Error " << errno << " on grantpt()\n";
std::abort();
}
returnCode = unlockpt(master);
if (returnCode != 0) {
std::cerr << "Error " << errno << " on unlockpt()\n";
std::abort();
}
int slave = open(ptsname(master), O_RDWR);
/////////////////////////////////////////////////////
// Create the child process
if (fork())
{
// FATHER
// Close the slave side of the PTY
close(slave);
return;
} else
{
struct termios slave_orig_term_settings; // Saved terminal settings
struct termios new_term_settings; // Current terminal settings
// CHILD
// Close the master side of the PTY
close(master);
// Save the defaults parameters of the slave side of the PTY
returnCode = tcgetattr(slave, &slave_orig_term_settings);
// Set RAW mode on slave side of PTY
new_term_settings = slave_orig_term_settings;
cfmakeraw (&new_term_settings);
tcsetattr (slave, TCSANOW, &new_term_settings);
// The slave side of the PTY becomes the standard input and outputs of the child process
close(0); // Close standard input (current terminal)
close(1); // Close standard output (current terminal)
close(2); // Close standard error (current terminal)
dup(slave); // PTY becomes standard input (0)
dup(slave); // PTY becomes standard output (1)
dup(slave); // PTY becomes standard error (2)
// Now the original file descriptor is useless
close(slave);
// Make the current process a new session leader
setsid();
// As the child is a session leader, set the controlling terminal to be the slave side of the PTY
// (Mandatory for programs like the shell to make them manage correctly their outputs)
ioctl(0, TIOCSCTTY, 1);
// Execution of the program
{
char* const* args = nullptr; char* const* args = nullptr;
returnCode = execvp("bash", args); execvp("bash", args);
} }
// if Error... //fcntl(master, F_SETFL, fcntl(master, F_GETFL, 0) | O_NONBLOCK);
std::abort(); /*
} if (master >= 0)
(void)write(master, "ls\n", 3);
///////////////////////////////////////////////////// */
} }
void Terminal::run() { void Terminal::run() {
static char input[150] = ""; static fd_set fds;
static std::vector<char> output(150); static int rc;
static std::string op = ""; static char opArr[150];
static int returnCode; static std::string input, output;
static std::future<int> rc; static timeval to; to = { 0, 0 };
static fd_set fd_in;
// Wait for data from standard input and master side of PTY FD_ZERO(&fds);
FD_ZERO(&fd_in); FD_SET(master, &fds);
FD_SET(master, &fd_in);
//returnCode = select(master + 1, &fd_in, NULL, NULL, NULL); rc = select(master + 1, &fds, NULL, NULL, &to);
rc = std::async(std::launch::async, [this](fd_set* f) -> int {
return select(master + 1, f, NULL, NULL, NULL);
}, &fd_in);
if(rc.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { switch(rc) {
switch(rc.get()) { case -1:
case -1: fprintf(stderr, "Error %d on select()\n", errno); std::cerr << "Error " << errno << " on select()\n";
exit(1); exit(1);
default:
default: { if(FD_ISSET(master, &fds)) {
// If data on master side of PTY rc = read(master, opArr, 150);
if(FD_ISSET(master, &fd_in)) { if(rc < 0) {
returnCode = read(master, output.data(), 150); //error on read
if (returnCode > 0) { app->stopModule(getName());
write(1, output.data(), returnCode); } else {
op += "\n"; if(input == "clear") output = "";
op += output.data(); output += opArr;
op += "\n"; }
output.clear(); }
} else {
if (returnCode < 0) {
fprintf(stderr, "Error %d on read master PTY\n", errno);
exit(1);
}
}
}
}
} // End switch
} }
ImGui::Begin("Terminal Module"); ImGui::Begin("Terminal Module");
ImGui::TextWrapped("%s", op.c_str()); ImGui::TextWrapped("%s", output.c_str());
ImGui::InputText("input", input, 150); ImGui::InputText("input", &input);
if(ImGui::Button("send")) { if(ImGui::Button("send")) {
static std::string s; s = input; s += "\n"; (void)write(master, (input + "\n").c_str(), input.length() + 1);
write(master, s.c_str(), s.length());
} }

View File

@@ -11,7 +11,7 @@ class Terminal : public Archimedes::Module {
void run(); void run();
private: private:
int master; int child_pid, master;
}; };
#ifdef TERMINAL_DYNAMIC #ifdef TERMINAL_DYNAMIC