Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 98 additions & 12 deletions backend/source/backend/process/fork_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/spdlog.h>

#include <algorithm>
#include <cctype>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <functional>
#include <mutex>
#include <string>
Expand Down Expand Up @@ -52,8 +55,8 @@ namespace
int writeFd{-1};
int sigFd{-1};
FdJsonIo io;
std::unordered_map<std::string, WProc> procs; // id -> WProc
std::unordered_map<int, std::string> fdToId; // pty master fd -> id
std::unordered_map<std::string, WProc> procs; // id -> WProc
std::unordered_map<int, std::string> fdToId; // pty master fd -> id
bool running{true};

WorkerState(int readFd_, int writeFd_)
Expand Down Expand Up @@ -197,6 +200,8 @@ namespace
handleResize(procId, msg["payload"]);
else if (cmd == "kill")
handleKill(procId);
else if (cmd == "listProcesses")
handleListProcesses(procId, msg["payload"]);
else
spdlog::warn("[worker] unknown command '{}' id='{}'", cmd, procId);
}
Expand Down Expand Up @@ -252,7 +257,9 @@ namespace
if (::openpty(&master, &slave, nullptr, &ttyOpts, &ws) == -1)
{
spdlog::error("[worker] openpty failed for id='{}': {}", procId, std::strerror(errno));
sendJson({{"id", procId}, {"type", "error"}, {"message", std::string{"openpty: "} + std::strerror(errno)}});
sendJson(
{{"id", procId}, {"type", "error"}, {"message", std::string{"openpty: "} + std::strerror(errno)}}
);
return;
}

Expand Down Expand Up @@ -288,7 +295,9 @@ namespace
::close(master);
::close(slave);
spdlog::error("[worker] fork failed for id='{}': {}", procId, std::strerror(errno));
sendJson({{"id", procId}, {"type", "error"}, {"message", std::string{"fork: "} + std::strerror(errno)}});
sendJson(
{{"id", procId}, {"type", "error"}, {"message", std::string{"fork: "} + std::strerror(errno)}}
);
return;
}

Expand All @@ -298,11 +307,7 @@ namespace
::close(master);
if (::login_tty(slave) == -1)
::_exit(127);
::execve(
exe.c_str(),
const_cast<char* const*>(argv.data()),
const_cast<char* const*>(envp.data())
);
::execve(exe.c_str(), const_cast<char* const*>(argv.data()), const_cast<char* const*>(envp.data()));
::_exit(127);
}

Expand Down Expand Up @@ -375,6 +380,88 @@ namespace
::ioctl(it->second.ptyMaster, TIOCSWINSZ, &ws);
}

void handleListProcesses(std::string const& procId, nlohmann::json const& payload)
{
const auto responseId = payload.value("responseId", std::string{});

const auto it = procs.find(procId);
if (it == procs.end() || it->second.ptyMaster < 0)
{
sendJson(
{{"id", procId},
{"type", "listProcesses"},
{"responseId", responseId},
{"error", "no such process"}}
);
return;
}

char slaveName[256];
if (::ptsname_r(it->second.ptyMaster, slaveName, sizeof(slaveName)) != 0)
{
sendJson(
{{"id", procId},
{"type", "listProcesses"},
{"responseId", responseId},
{"error", std::string{"ptsname_r: "} + std::strerror(errno)}}
);
return;
}

nlohmann::json procsList = nlohmann::json::array();
try
{
for (const auto& entry : std::filesystem::directory_iterator("/proc"))
{
try
{
if (!entry.is_directory())
continue;
const std::string pidStr = entry.path().filename().string();
if (!std::all_of(
pidStr.begin(),
pidStr.end(),
[](unsigned char chr)
{
return std::isdigit(chr);
}
))
continue;
const auto fdPath = entry.path() / "fd" / "0";
if (!std::filesystem::is_symlink(fdPath))
continue;
std::error_code ec;
const auto target = std::filesystem::read_symlink(fdPath, ec);
if (ec || target.string() != slaveName)
continue;
std::ifstream cmdlineFile{entry.path() / "cmdline"};
if (!cmdlineFile)
continue;
std::string cmdline;
std::getline(cmdlineFile, cmdline, '\0');
procsList.push_back({{"pid", std::stoi(pidStr)}, {"cmdline", cmdline}});
}
catch (...)
{
continue;
}
}
}
catch (...)
{}

std::sort(
procsList.begin(),
procsList.end(),
[](nlohmann::json const& a, nlohmann::json const& b)
{
return a["pid"].get<int>() < b["pid"].get<int>();
}
);

sendJson({{"id", procId}, {"type", "listProcesses"}, {"responseId", responseId}, {"procs", procsList}});
}

void handleKill(std::string const& procId)
{
auto it = procs.find(procId);
Expand Down Expand Up @@ -456,9 +543,8 @@ namespace
auto logPath = Nui::resolvePath("%state_home2%/nui-sftp/logs/worker.log");
std::error_code mkdirEc;
std::filesystem::create_directories(logPath.parent_path(), mkdirEc);
auto fileSink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
logPath.string(), 2 * 1024 * 1024, 3, true
);
auto fileSink =
std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logPath.string(), 2 * 1024 * 1024, 3, true);
fileSink->set_level(spdlog::level::trace);
auto logger = std::make_shared<spdlog::logger>("worker", std::move(fileSink));
logger->set_level(spdlog::level::trace);
Expand Down
58 changes: 55 additions & 3 deletions backend/source/backend/process/process_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,40 @@ void ProcessStore::handleWorkerMessage(nlohmann::json const& msg)
wnd_->runInJavascriptThread(
[this, procId]()
{
hub_->callRemote("SessionArea::processDied", nlohmann::json{{"id", procId}});
hub_->callRemote("execTerminalExit_" + procId, nlohmann::json{{"id", procId}});
}
);
}
else if (type == "listProcesses")
{
const auto responseId = msg.value("responseId", std::string{});
if (msg.contains("error"))
{
const auto errMsg = msg["error"].get<std::string>();
wnd_->runInJavascriptThread(
[this, responseId, errMsg]()
{
hub_->callRemote(responseId, nlohmann::json{{"error", errMsg}});
}
);
return;
}
const auto procsList = msg["procs"];
wnd_->runInJavascriptThread(
[this, responseId, procsList]()
{
if (procsList.empty())
{
hub_->callRemote(responseId, nlohmann::json{{"error", "No processes found"}});
return;
}
nlohmann::json j = nlohmann::json::object();
j["latest"] = {
{"pid", procsList.front()["pid"]},
{"cmdline", procsList.front()["cmdline"]},
};
j["all"] = procsList;
hub_->callRemote(responseId, j);
}
);
}
Expand Down Expand Up @@ -233,7 +266,7 @@ void ProcessStore::notifyChildExit(Nui::RpcHub& hub, std::string const& id)

process->second->exit();
processes_.erase(process);
hub.callRemote("SessionArea::processDied", nlohmann::json{{"id", id}});
hub.callRemote("execTerminalExit_" + id, nlohmann::json{{"id", id}});
}

void ProcessStore::notifyChildExit(Nui::RpcHub& hub, long long pid)
Expand All @@ -245,7 +278,7 @@ void ProcessStore::notifyChildExit(Nui::RpcHub& hub, long long pid)
process->exit();
const auto idCopy = id;
processes_.erase(id);
hub.callRemote("SessionArea::processDied", nlohmann::json{{"id", idCopy}, {"pid", pid}});
hub.callRemote("execTerminalExit_" + idCopy, nlohmann::json{{"id", idCopy}, {"pid", pid}});
return;
}
}
Expand Down Expand Up @@ -549,6 +582,25 @@ void ProcessStore::registerRpc(Nui::Window& wnd, Nui::RpcHub& hub)
{
try
{
#ifndef _WIN32
{
auto iter = forkPoolProcesses_.find(id);
if (iter != forkPoolProcesses_.end())
{
if (forkPool_)
forkPool_->send(
nlohmann::json{
{"id", id},
{"command", "listProcesses"},
{"payload", {{"responseId", responseId}}},
}
);
else
hub->callRemote(responseId, nlohmann::json{{"error", "No fork pool available"}});
return;
}
}
#endif
auto process = processes_.find(id);
if (process == processes_.end())
{
Expand Down
4 changes: 2 additions & 2 deletions frontend/include/frontend/session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ class Session
void createExecutingEngine();
void createSshEngine();

NuiFileExplorer::Side& remoteFileGridSide();
NuiFileExplorer::Side& localFileGridSide();
NuiFileExplorer::Side* remoteFileGridSide();

RemoteSideModel& remoteSideModel();
LocalSideModel& localSideModel();
RemoteSideModel* remoteSideModel();

void loadLayoutExtras(nlohmann::json const& layoutExtra);

Expand Down
17 changes: 12 additions & 5 deletions frontend/include/frontend/terminal/executing_channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <frontend/terminal/channel_interface.hpp>
#include <ids/ids.hpp>
#include <nui/rpc.hpp>
#include <nui/frontend/api/timer.hpp>

#include <functional>
#include <string>
Expand All @@ -18,16 +19,18 @@ class ExecutingChannel : public ChannelInterface
{
public:
/**
* @param channelId The process UUID returned by ProcessStore::spawn.
* @param stdoutReceiver Already-registered receiver for stdout data.
* @param stderrReceiver Already-registered receiver for stderr data.
* @param exitReceiver Already-registered receiver for process exit.
* @param channelId The process UUID returned by ProcessStore::spawn.
* @param stdoutReceiver Already-registered receiver for stdout data.
* @param stderrReceiver Already-registered receiver for stderr data.
* @param exitReceiver Already-registered receiver for process exit.
* @param onProcessChange Called with the channel id and current foreground process cmdline after writes.
*/
ExecutingChannel(
Ids::ChannelId channelId,
Nui::RpcClient::AutoUnregister stdoutReceiver,
Nui::RpcClient::AutoUnregister stderrReceiver,
Nui::RpcClient::AutoUnregister exitReceiver
Nui::RpcClient::AutoUnregister exitReceiver,
std::function<void(Ids::ChannelId const&, std::string const&)> onProcessChange
);
~ExecutingChannel() override = default;
ExecutingChannel(ExecutingChannel&&) = default;
Expand Down Expand Up @@ -55,8 +58,12 @@ class ExecutingChannel : public ChannelInterface
}

private:
void updatePtyProcs();

Ids::ChannelId channelId_;
Nui::RpcClient::AutoUnregister stdoutReceiver_;
Nui::RpcClient::AutoUnregister stderrReceiver_;
Nui::RpcClient::AutoUnregister exitReceiver_;
std::function<void(Ids::ChannelId const&, std::string const&)> onProcessChange_;
Nui::TimerHandle procInfoTimer_;
};
3 changes: 2 additions & 1 deletion frontend/include/frontend/terminal/executing_engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <frontend/terminal/terminal_engine.hpp>
#include <roar/detail/pimpl_special_functions.hpp>
#include <persistence/state/session_options.hpp>
#include <ids/ids.hpp>
#include <nui/utility/move_detector.hpp>

#include <memory>
Expand All @@ -24,7 +25,7 @@ class ExecutingTerminalEngine : public TerminalEngine
{
Persistence::ExecutingSessionOptions engineOptions;
Persistence::Termios termios;
std::function<void(std::string)> onProcessChange;
std::function<void(Ids::ChannelId const&, std::string)> onProcessChange;
};

public:
Expand Down
Loading