Support piping data to client stdin
Pass the client stdin fd to the server and open a fifo buffer from it. Fixes #3394
This commit is contained in:
parent
2104af0771
commit
60154300f9
|
@ -265,4 +265,15 @@ Vector<String> undo_group_as_strings(const Buffer::UndoGroup& undo_group)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String generate_buffer_name(StringView pattern)
|
||||||
|
{
|
||||||
|
auto& buffer_manager = BufferManager::instance();
|
||||||
|
for (int i = 0; true; ++i)
|
||||||
|
{
|
||||||
|
String name = format(pattern, i);
|
||||||
|
if (buffer_manager.get_buffer_ifp(name) == nullptr)
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,8 @@ void write_to_debug_buffer(StringView str);
|
||||||
Vector<String> history_as_strings(const Vector<Buffer::HistoryNode>& history);
|
Vector<String> history_as_strings(const Vector<Buffer::HistoryNode>& history);
|
||||||
Vector<String> undo_group_as_strings(const Buffer::UndoGroup& undo_group);
|
Vector<String> undo_group_as_strings(const Buffer::UndoGroup& undo_group);
|
||||||
|
|
||||||
|
String generate_buffer_name(StringView pattern);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // buffer_utils_hh_INCLUDED
|
#endif // buffer_utils_hh_INCLUDED
|
||||||
|
|
|
@ -356,16 +356,8 @@ void edit(const ParametersParser& parser, Context& context, const ShellContext&)
|
||||||
(parser.get_switch("debug") ? Buffer::Flags::Debug : Buffer::Flags::None);
|
(parser.get_switch("debug") ? Buffer::Flags::Debug : Buffer::Flags::None);
|
||||||
|
|
||||||
auto& buffer_manager = BufferManager::instance();
|
auto& buffer_manager = BufferManager::instance();
|
||||||
auto generate_scratch_name = [&] {
|
|
||||||
for (int i = 0; true; ++i)
|
|
||||||
{
|
|
||||||
String name = format("*scratch-{}*", i);
|
|
||||||
if (buffer_manager.get_buffer_ifp(name) == nullptr)
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const auto& name = parser.positional_count() > 0 ?
|
const auto& name = parser.positional_count() > 0 ?
|
||||||
parser[0] : (scratch ? generate_scratch_name() : context.buffer().name());
|
parser[0] : (scratch ? generate_buffer_name("*scratch-{}*") : context.buffer().name());
|
||||||
|
|
||||||
Buffer* buffer = buffer_manager.get_buffer_ifp(name);
|
Buffer* buffer = buffer_manager.get_buffer_ifp(name);
|
||||||
if (scratch)
|
if (scratch)
|
||||||
|
|
14
src/main.cc
14
src/main.cc
|
@ -661,9 +661,21 @@ int run_client(StringView session, StringView name, StringView client_init,
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Optional<int> stdin_fd;
|
||||||
|
if (not isatty(0))
|
||||||
|
{
|
||||||
|
// move stdin to another fd, and restore tty as stdin
|
||||||
|
stdin_fd = dup(0);
|
||||||
|
int tty = open("/dev/tty", O_RDONLY);
|
||||||
|
dup2(tty, 0);
|
||||||
|
close(tty);
|
||||||
|
}
|
||||||
|
|
||||||
EventManager event_manager;
|
EventManager event_manager;
|
||||||
RemoteClient client{session, name, make_ui(ui_type), getpid(), get_env_vars(),
|
RemoteClient client{session, name, make_ui(ui_type), getpid(), get_env_vars(),
|
||||||
client_init, std::move(init_coord)};
|
client_init, std::move(init_coord), stdin_fd};
|
||||||
|
stdin_fd.map(close);
|
||||||
|
|
||||||
if (suspend)
|
if (suspend)
|
||||||
raise(SIGTSTP);
|
raise(SIGTSTP);
|
||||||
while (not client.exit_status() and client.is_ui_ok())
|
while (not client.exit_status() and client.is_ui_ok())
|
||||||
|
|
|
@ -60,11 +60,12 @@ public:
|
||||||
bool operator!=(const Optional& other) const { return !(*this == other); }
|
bool operator!=(const Optional& other) const { return !(*this == other); }
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void emplace(Args&&... args)
|
T& emplace(Args&&... args)
|
||||||
{
|
{
|
||||||
destruct_ifn();
|
destruct_ifn();
|
||||||
new (&m_value) T{std::forward<Args>(args)...};
|
new (&m_value) T{std::forward<Args>(args)...};
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
|
return m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
T& operator*()
|
T& operator*()
|
||||||
|
|
|
@ -261,25 +261,56 @@ public:
|
||||||
return Reader<T>::read(*this);
|
return Reader<T>::read(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<int> ancillary_fd()
|
||||||
|
{
|
||||||
|
auto res = m_ancillary_fd;
|
||||||
|
m_ancillary_fd.reset();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MsgReader()
|
||||||
|
{
|
||||||
|
m_ancillary_fd.map(close);
|
||||||
|
}
|
||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
{
|
{
|
||||||
m_stream.resize(0);
|
m_stream.resize(0);
|
||||||
m_write_pos = 0;
|
m_write_pos = 0;
|
||||||
m_read_pos = header_size;
|
m_read_pos = header_size;
|
||||||
|
m_ancillary_fd.map(close);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void read_from_socket(int sock, size_t size)
|
void read_from_socket(int sock, size_t size)
|
||||||
{
|
{
|
||||||
kak_assert(m_write_pos + size <= m_stream.size());
|
kak_assert(m_write_pos + size <= m_stream.size());
|
||||||
int res = ::read(sock, m_stream.data() + m_write_pos, size);
|
iovec io{m_stream.data() + m_write_pos, size};
|
||||||
|
alignas(cmsghdr) char fdbuf[CMSG_SPACE(sizeof(int))];
|
||||||
|
|
||||||
|
msghdr msg{};
|
||||||
|
msg.msg_iov = &io;
|
||||||
|
msg.msg_iovlen = 1;
|
||||||
|
msg.msg_control = fdbuf;
|
||||||
|
msg.msg_controllen = sizeof(fdbuf);
|
||||||
|
|
||||||
|
int res = recvmsg(sock, &msg, MSG_CMSG_CLOEXEC);
|
||||||
if (res <= 0)
|
if (res <= 0)
|
||||||
throw disconnected{format("socket read failed: {}", strerror(errno))};
|
throw disconnected{format("socket read failed: {}", strerror(errno))};
|
||||||
|
|
||||||
m_write_pos += res;
|
m_write_pos += res;
|
||||||
|
|
||||||
|
if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||||
|
cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
|
||||||
|
{
|
||||||
|
m_ancillary_fd.map(close);
|
||||||
|
memcpy(&m_ancillary_fd.emplace(), CMSG_DATA(cmsg), sizeof(int));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr uint32_t header_size = sizeof(MessageType) + sizeof(uint32_t);
|
static constexpr uint32_t header_size = sizeof(MessageType) + sizeof(uint32_t);
|
||||||
Vector<char, MemoryDomain::Remote> m_stream;
|
Vector<char, MemoryDomain::Remote> m_stream;
|
||||||
|
Optional<int> m_ancillary_fd;
|
||||||
uint32_t m_write_pos = 0;
|
uint32_t m_write_pos = 0;
|
||||||
uint32_t m_read_pos = header_size;
|
uint32_t m_read_pos = header_size;
|
||||||
};
|
};
|
||||||
|
@ -398,14 +429,32 @@ private:
|
||||||
RemoteBuffer m_send_buffer;
|
RemoteBuffer m_send_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool send_data(int fd, RemoteBuffer& buffer)
|
static bool send_data(int fd, RemoteBuffer& buffer, Optional<int> ancillary_fd = {})
|
||||||
{
|
{
|
||||||
while (not buffer.empty() and fd_writable(fd))
|
while (not buffer.empty() and fd_writable(fd))
|
||||||
{
|
{
|
||||||
int res = ::write(fd, buffer.data(), buffer.size());
|
iovec io{buffer.data(), buffer.size()};
|
||||||
if (res <= 0)
|
alignas(cmsghdr) char fdbuf[CMSG_SPACE(sizeof(int))];
|
||||||
throw disconnected{format("socket write failed: {}", strerror(errno))};
|
|
||||||
buffer.erase(buffer.begin(), buffer.begin() + res);
|
msghdr msg{};
|
||||||
|
msg.msg_iov = &io;
|
||||||
|
msg.msg_iovlen = 1;
|
||||||
|
if (ancillary_fd)
|
||||||
|
{
|
||||||
|
msg.msg_control = fdbuf;
|
||||||
|
msg.msg_controllen = sizeof(fdbuf);
|
||||||
|
|
||||||
|
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||||
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||||
|
cmsg->cmsg_level = SOL_SOCKET;
|
||||||
|
cmsg->cmsg_type = SCM_RIGHTS;
|
||||||
|
memcpy(CMSG_DATA(cmsg), &*ancillary_fd, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = sendmsg(fd, &msg, 0);
|
||||||
|
if (res <= 0)
|
||||||
|
throw disconnected{format("socket write failed: {}", strerror(errno))};
|
||||||
|
buffer.erase(buffer.begin(), buffer.begin() + res);
|
||||||
}
|
}
|
||||||
return buffer.empty();
|
return buffer.empty();
|
||||||
}
|
}
|
||||||
|
@ -592,7 +641,7 @@ bool check_session(StringView session)
|
||||||
|
|
||||||
RemoteClient::RemoteClient(StringView session, StringView name, std::unique_ptr<UserInterface>&& ui,
|
RemoteClient::RemoteClient(StringView session, StringView name, std::unique_ptr<UserInterface>&& ui,
|
||||||
int pid, const EnvVarMap& env_vars, StringView init_command,
|
int pid, const EnvVarMap& env_vars, StringView init_command,
|
||||||
Optional<BufferCoord> init_coord)
|
Optional<BufferCoord> init_coord, Optional<int> stdin_fd)
|
||||||
: m_ui(std::move(ui))
|
: m_ui(std::move(ui))
|
||||||
{
|
{
|
||||||
int sock = connect_to(session);
|
int sock = connect_to(session);
|
||||||
|
@ -601,6 +650,7 @@ RemoteClient::RemoteClient(StringView session, StringView name, std::unique_ptr<
|
||||||
MsgWriter msg{m_send_buffer, MessageType::Connect};
|
MsgWriter msg{m_send_buffer, MessageType::Connect};
|
||||||
msg.write(pid, name, init_command, init_coord, m_ui->dimensions(), env_vars);
|
msg.write(pid, name, init_command, init_coord, m_ui->dimensions(), env_vars);
|
||||||
}
|
}
|
||||||
|
send_data(sock, m_send_buffer, stdin_fd);
|
||||||
|
|
||||||
m_ui->set_on_key([this](Key key){
|
m_ui->set_on_key([this](Key key){
|
||||||
MsgWriter msg(m_send_buffer, MessageType::Key);
|
MsgWriter msg(m_send_buffer, MessageType::Key);
|
||||||
|
@ -750,6 +800,10 @@ private:
|
||||||
auto init_coord = m_reader.read<Optional<BufferCoord>>();
|
auto init_coord = m_reader.read<Optional<BufferCoord>>();
|
||||||
auto dimensions = m_reader.read<DisplayCoord>();
|
auto dimensions = m_reader.read<DisplayCoord>();
|
||||||
auto env_vars = m_reader.read<HashMap<String, String, MemoryDomain::EnvVars>>();
|
auto env_vars = m_reader.read<HashMap<String, String, MemoryDomain::EnvVars>>();
|
||||||
|
|
||||||
|
if (auto stdin_fd = m_reader.ancillary_fd())
|
||||||
|
create_fifo_buffer(generate_buffer_name("*stdin-{}*"), *stdin_fd, Buffer::Flags::None);
|
||||||
|
|
||||||
auto* ui = new RemoteUI{sock, dimensions};
|
auto* ui = new RemoteUI{sock, dimensions};
|
||||||
ClientManager::instance().create_client(
|
ClientManager::instance().create_client(
|
||||||
std::unique_ptr<UserInterface>(ui), pid, std::move(name),
|
std::unique_ptr<UserInterface>(ui), pid, std::move(name),
|
||||||
|
|
|
@ -32,7 +32,7 @@ class RemoteClient
|
||||||
public:
|
public:
|
||||||
RemoteClient(StringView session, StringView name, std::unique_ptr<UserInterface>&& ui,
|
RemoteClient(StringView session, StringView name, std::unique_ptr<UserInterface>&& ui,
|
||||||
int pid, const EnvVarMap& env_vars, StringView init_command,
|
int pid, const EnvVarMap& env_vars, StringView init_command,
|
||||||
Optional<BufferCoord> init_coord);
|
Optional<BufferCoord> init_coord, Optional<int> stdin_fd);
|
||||||
|
|
||||||
bool is_ui_ok() const;
|
bool is_ui_ok() const;
|
||||||
const Optional<int>& exit_status() const { return m_exit_status; }
|
const Optional<int>& exit_status() const { return m_exit_status; }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user