use strings and forkpty
This commit is contained in:
@@ -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 \
|
||||||
|
|||||||
@@ -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);
|
|
||||||
op += "\n";
|
|
||||||
op += output.data();
|
|
||||||
op += "\n";
|
|
||||||
output.clear();
|
|
||||||
} else {
|
} else {
|
||||||
if (returnCode < 0) {
|
if(input == "clear") output = "";
|
||||||
fprintf(stderr, "Error %d on read master PTY\n", errno);
|
output += opArr;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user