mirror of
https://github.com/tmate-io/tmate-ssh-server.git
synced 2020-11-18 19:53:51 -08:00
Client side first shot
This commit is contained in:
parent
1b083aa0fd
commit
220b2afb3c
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,3 +15,6 @@ tmux
|
|||||||
Makefile
|
Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
configure
|
configure
|
||||||
|
tmate
|
||||||
|
cscope.*
|
||||||
|
ctags
|
||||||
|
59
Makefile.am
59
Makefile.am
@ -1,8 +1,8 @@
|
|||||||
# $Id$
|
# $Id$
|
||||||
|
|
||||||
# Obvious program stuff.
|
# Obvious program stuff.
|
||||||
bin_PROGRAMS = tmux
|
bin_PROGRAMS = tmate
|
||||||
dist_man1_MANS = tmux.1
|
dist_man1_MANS = tmate.1
|
||||||
|
|
||||||
# Distribution tarball options.
|
# Distribution tarball options.
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
@ -21,6 +21,8 @@ if IS_GLIBC
|
|||||||
CFLAGS += -D_GNU_SOURCE
|
CFLAGS += -D_GNU_SOURCE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
CFLAGS += -Wno-unused-parameter -Wno-unused-variable
|
||||||
|
|
||||||
# Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly
|
# Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly
|
||||||
# different flags.
|
# different flags.
|
||||||
if IS_GCC
|
if IS_GCC
|
||||||
@ -57,7 +59,7 @@ CFLAGS += -erroff=E_EMPTY_DECLARATION
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# List of sources.
|
# List of sources.
|
||||||
dist_tmux_SOURCES = \
|
dist_tmate_SOURCES = \
|
||||||
arguments.c \
|
arguments.c \
|
||||||
attributes.c \
|
attributes.c \
|
||||||
cfg.c \
|
cfg.c \
|
||||||
@ -171,6 +173,10 @@ dist_tmux_SOURCES = \
|
|||||||
session.c \
|
session.c \
|
||||||
signal.c \
|
signal.c \
|
||||||
status.c \
|
status.c \
|
||||||
|
tmate-ssh-client.c \
|
||||||
|
tmate-encoder.c \
|
||||||
|
tmate-decoder.c \
|
||||||
|
tmate.c \
|
||||||
tmux.c \
|
tmux.c \
|
||||||
tty-acs.c \
|
tty-acs.c \
|
||||||
tty-keys.c \
|
tty-keys.c \
|
||||||
@ -183,66 +189,51 @@ dist_tmux_SOURCES = \
|
|||||||
window.c \
|
window.c \
|
||||||
xmalloc.c \
|
xmalloc.c \
|
||||||
xterm-keys.c
|
xterm-keys.c
|
||||||
nodist_tmux_SOURCES = osdep-@PLATFORM@.c
|
nodist_tmate_SOURCES = osdep-@PLATFORM@.c
|
||||||
|
|
||||||
# Pile in all the compat/ stuff that is needed.
|
# Pile in all the compat/ stuff that is needed.
|
||||||
if NO_FORKPTY
|
if NO_FORKPTY
|
||||||
nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c
|
nodist_tmate_SOURCES += compat/forkpty-@PLATFORM@.c
|
||||||
endif
|
endif
|
||||||
if NO_IMSG
|
if NO_IMSG
|
||||||
nodist_tmux_SOURCES += compat/imsg.c compat/imsg-buffer.c
|
nodist_tmate_SOURCES += compat/imsg.c compat/imsg-buffer.c
|
||||||
endif
|
endif
|
||||||
if NO_CLOSEFROM
|
if NO_CLOSEFROM
|
||||||
nodist_tmux_SOURCES += compat/closefrom.c
|
nodist_tmate_SOURCES += compat/closefrom.c
|
||||||
endif
|
endif
|
||||||
if NO_DAEMON
|
if NO_DAEMON
|
||||||
nodist_tmux_SOURCES += compat/daemon.c
|
nodist_tmate_SOURCES += compat/daemon.c
|
||||||
endif
|
endif
|
||||||
if NO_SETENV
|
if NO_SETENV
|
||||||
nodist_tmux_SOURCES += compat/setenv.c
|
nodist_tmate_SOURCES += compat/setenv.c
|
||||||
endif
|
endif
|
||||||
if NO_STRLCAT
|
if NO_STRLCAT
|
||||||
nodist_tmux_SOURCES += compat/strlcat.c
|
nodist_tmate_SOURCES += compat/strlcat.c
|
||||||
endif
|
endif
|
||||||
if NO_STRLCPY
|
if NO_STRLCPY
|
||||||
nodist_tmux_SOURCES += compat/strlcpy.c
|
nodist_tmate_SOURCES += compat/strlcpy.c
|
||||||
endif
|
endif
|
||||||
if NO_ASPRINTF
|
if NO_ASPRINTF
|
||||||
nodist_tmux_SOURCES += compat/asprintf.c
|
nodist_tmate_SOURCES += compat/asprintf.c
|
||||||
endif
|
endif
|
||||||
if NO_FGETLN
|
if NO_FGETLN
|
||||||
nodist_tmux_SOURCES += compat/fgetln.c
|
nodist_tmate_SOURCES += compat/fgetln.c
|
||||||
endif
|
endif
|
||||||
if NO_GETOPT
|
if NO_GETOPT
|
||||||
nodist_tmux_SOURCES += compat/getopt.c
|
nodist_tmate_SOURCES += compat/getopt.c
|
||||||
endif
|
endif
|
||||||
if NO_STRCASESTR
|
if NO_STRCASESTR
|
||||||
nodist_tmux_SOURCES += compat/strcasestr.c
|
nodist_tmate_SOURCES += compat/strcasestr.c
|
||||||
endif
|
endif
|
||||||
if NO_STRSEP
|
if NO_STRSEP
|
||||||
nodist_tmux_SOURCES += compat/strsep.c
|
nodist_tmate_SOURCES += compat/strsep.c
|
||||||
endif
|
endif
|
||||||
if NO_VIS
|
if NO_VIS
|
||||||
nodist_tmux_SOURCES += compat/vis.c compat/unvis.c
|
nodist_tmate_SOURCES += compat/vis.c compat/unvis.c
|
||||||
endif
|
endif
|
||||||
if NO_STRTONUM
|
if NO_STRTONUM
|
||||||
nodist_tmux_SOURCES += compat/strtonum.c
|
nodist_tmate_SOURCES += compat/strtonum.c
|
||||||
endif
|
endif
|
||||||
if NO_B64_NTOP
|
if NO_B64_NTOP
|
||||||
nodist_tmux_SOURCES += compat/b64_ntop.c
|
nodist_tmate_SOURCES += compat/b64_ntop.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Update SF web site.
|
|
||||||
upload-index.html: update-index.html
|
|
||||||
scp www/index.html www/main.css www/images/*.png \
|
|
||||||
${USER},tmux@web.sf.net:/home/groups/t/tm/tmux/htdocs
|
|
||||||
rm -f www/index.html www/images/small-*
|
|
||||||
|
|
||||||
update-index.html:
|
|
||||||
(cd www/images && \
|
|
||||||
rm -f small-* && \
|
|
||||||
for i in *.png; do \
|
|
||||||
convert "$$i" -resize 200x150 "small-$$i"; \
|
|
||||||
done \
|
|
||||||
)
|
|
||||||
sed "s/%%VERSION%%/${VERSION}/g" www/index.html.in >www/index.html
|
|
||||||
|
@ -41,6 +41,10 @@ const struct cmd_entry cmd_break_pane_entry = {
|
|||||||
enum cmd_retval
|
enum cmd_retval
|
||||||
cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||||
{
|
{
|
||||||
|
#ifdef TMATE
|
||||||
|
cmdq_error(cmdq, "break pane is not supported with tmate");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
#else
|
||||||
struct args *args = self->args;
|
struct args *args = self->args;
|
||||||
struct winlink *wl;
|
struct winlink *wl;
|
||||||
struct session *s;
|
struct session *s;
|
||||||
@ -112,4 +116,5 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
|||||||
format_free(ft);
|
format_free(ft);
|
||||||
}
|
}
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
|||||||
enum cmd_retval
|
enum cmd_retval
|
||||||
join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
|
join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
|
||||||
{
|
{
|
||||||
|
#ifdef TMATE
|
||||||
|
cmdq_error(cmdq, "join pane is not supported with tmate");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
#else
|
||||||
struct args *args = self->args;
|
struct args *args = self->args;
|
||||||
struct session *dst_s;
|
struct session *dst_s;
|
||||||
struct winlink *src_wl, *dst_wl;
|
struct winlink *src_wl, *dst_wl;
|
||||||
@ -170,4 +174,5 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
|
|||||||
|
|
||||||
notify_window_layout_changed(dst_w);
|
notify_window_layout_changed(dst_w);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,9 @@ AC_MSG_RESULT($found_glibc)
|
|||||||
# Look for clock_gettime. Must come before event_init.
|
# Look for clock_gettime. Must come before event_init.
|
||||||
AC_SEARCH_LIBS(clock_gettime, rt)
|
AC_SEARCH_LIBS(clock_gettime, rt)
|
||||||
|
|
||||||
|
AC_SEARCH_LIBS(ssh_new, ssh)
|
||||||
|
AC_SEARCH_LIBS(msgpack_object_print, msgpack)
|
||||||
|
|
||||||
# Look for libevent.
|
# Look for libevent.
|
||||||
PKG_CHECK_MODULES(
|
PKG_CHECK_MODULES(
|
||||||
LIBEVENT,
|
LIBEVENT,
|
||||||
|
12
resize.c
12
resize.c
@ -21,6 +21,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recalculate window and session sizes.
|
* Recalculate window and session sizes.
|
||||||
@ -71,6 +72,17 @@ recalculate_sizes(void)
|
|||||||
ssy = c->tty.sy;
|
ssy = c->tty.sy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TMATE
|
||||||
|
/* We assume a single session */
|
||||||
|
if (tmate_sx > 0 && tmate_sy > 0) {
|
||||||
|
if ((u_int)tmate_sx < ssx)
|
||||||
|
ssx = tmate_sx;
|
||||||
|
if ((u_int)tmate_sy < ssy)
|
||||||
|
ssy = tmate_sy;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ssx == UINT_MAX || ssy == UINT_MAX) {
|
if (ssx == UINT_MAX || ssy == UINT_MAX) {
|
||||||
s->flags |= SESSION_UNATTACHED;
|
s->flags |= SESSION_UNATTACHED;
|
||||||
continue;
|
continue;
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
void server_client_check_focus(struct window_pane *);
|
void server_client_check_focus(struct window_pane *);
|
||||||
void server_client_check_resize(struct window_pane *);
|
void server_client_check_resize(struct window_pane *);
|
||||||
@ -511,6 +512,9 @@ server_client_loop(void)
|
|||||||
if (w == NULL)
|
if (w == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (w->flags & WINDOW_REDRAW)
|
||||||
|
tmate_sync_window(w);
|
||||||
|
|
||||||
w->flags &= ~WINDOW_REDRAW;
|
w->flags &= ~WINDOW_REDRAW;
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
server_client_check_focus(wp);
|
server_client_check_focus(wp);
|
||||||
|
4
server.c
4
server.c
@ -36,6 +36,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Main server functions.
|
* Main server functions.
|
||||||
@ -195,6 +196,9 @@ server_start(int lockfd, char *lockfile)
|
|||||||
evtimer_add(&server_ev_second, &tv);
|
evtimer_add(&server_ev_second, &tv);
|
||||||
|
|
||||||
set_signals(server_signal_callback);
|
set_signals(server_signal_callback);
|
||||||
|
|
||||||
|
tmate_client_start();
|
||||||
|
|
||||||
server_loop();
|
server_loop();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
/* Global session list. */
|
/* Global session list. */
|
||||||
struct sessions sessions;
|
struct sessions sessions;
|
||||||
@ -90,6 +91,11 @@ session_create(const char *name, const char *cmd, const char *cwd,
|
|||||||
{
|
{
|
||||||
struct session *s;
|
struct session *s;
|
||||||
|
|
||||||
|
if (next_session_id != 0) {
|
||||||
|
xasprintf(cause, "multi sessions is not supported with tmate");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
s = xmalloc(sizeof *s);
|
s = xmalloc(sizeof *s);
|
||||||
s->references = 0;
|
s->references = 0;
|
||||||
s->flags = 0;
|
s->flags = 0;
|
||||||
|
118
tmate-decoder.c
Normal file
118
tmate-decoder.c
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
|
int tmate_sx = -1;
|
||||||
|
int tmate_sy = -1;
|
||||||
|
|
||||||
|
struct tmate_unpacker {
|
||||||
|
msgpack_object *argv;
|
||||||
|
int argc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void decoder_error(void)
|
||||||
|
{
|
||||||
|
tmate_fatal("Received a bad message");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_unpacker(struct tmate_unpacker *uk,
|
||||||
|
msgpack_object obj)
|
||||||
|
{
|
||||||
|
if (obj.type != MSGPACK_OBJECT_ARRAY)
|
||||||
|
decoder_error();
|
||||||
|
|
||||||
|
uk->argv = obj.via.array.ptr;
|
||||||
|
uk->argc = obj.via.array.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t unpack_int(struct tmate_unpacker *uk)
|
||||||
|
{
|
||||||
|
int64_t val;
|
||||||
|
|
||||||
|
if (uk->argc == 0)
|
||||||
|
decoder_error();
|
||||||
|
|
||||||
|
if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER &&
|
||||||
|
uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER)
|
||||||
|
decoder_error();
|
||||||
|
|
||||||
|
val = uk->argv[0].via.i64;
|
||||||
|
|
||||||
|
uk->argv++;
|
||||||
|
uk->argc--;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tmate_client_key(struct tmate_unpacker *uk)
|
||||||
|
{
|
||||||
|
struct client *c;
|
||||||
|
int key = unpack_int(uk);
|
||||||
|
|
||||||
|
/* Very gross. other clients cannot even detach */
|
||||||
|
|
||||||
|
if (ARRAY_LENGTH(&clients) > 0) {
|
||||||
|
c = ARRAY_ITEM(&clients, 0);
|
||||||
|
server_client_handle_key(c, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tmate_client_resize(struct tmate_unpacker *uk)
|
||||||
|
{
|
||||||
|
/* A bit gross as well */
|
||||||
|
tmate_sx = unpack_int(uk);
|
||||||
|
tmate_sy = unpack_int(uk);
|
||||||
|
recalculate_sizes();
|
||||||
|
|
||||||
|
/* TODO Handle reconnection cases */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_message(msgpack_object obj)
|
||||||
|
{
|
||||||
|
struct tmate_unpacker _uk;
|
||||||
|
struct tmate_unpacker *uk = &_uk;
|
||||||
|
int cmd;
|
||||||
|
|
||||||
|
init_unpacker(uk, obj);
|
||||||
|
|
||||||
|
switch (unpack_int(uk)) {
|
||||||
|
case TMATE_CLIENT_KEY: tmate_client_key(uk); break;
|
||||||
|
case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break;
|
||||||
|
default: decoder_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len)
|
||||||
|
{
|
||||||
|
msgpack_unpacked result;
|
||||||
|
|
||||||
|
msgpack_unpacker_buffer_consumed(&decoder->unpacker, len);
|
||||||
|
|
||||||
|
msgpack_unpacked_init(&result);
|
||||||
|
while (msgpack_unpacker_next(&decoder->unpacker, &result)) {
|
||||||
|
handle_message(result.data);
|
||||||
|
}
|
||||||
|
msgpack_unpacked_destroy(&result);
|
||||||
|
|
||||||
|
if (msgpack_unpacker_message_size(&decoder->unpacker) >
|
||||||
|
TMATE_MAX_MESSAGE_SIZE) {
|
||||||
|
tmate_fatal("Message too big");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_decoder_get_buffer(struct tmate_decoder *decoder,
|
||||||
|
char **buf, size_t *len)
|
||||||
|
{
|
||||||
|
/* rewind the buffer if possible */
|
||||||
|
if (msgpack_unpacker_buffer_capacity(&decoder->unpacker) <
|
||||||
|
TMATE_MAX_MESSAGE_SIZE) {
|
||||||
|
msgpack_unpacker_expand_buffer(&decoder->unpacker, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf = msgpack_unpacker_buffer(&decoder->unpacker);
|
||||||
|
*len = msgpack_unpacker_buffer_capacity(&decoder->unpacker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_decoder_init(struct tmate_decoder *decoder)
|
||||||
|
{
|
||||||
|
if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE))
|
||||||
|
tmate_fatal("cannot initialize the unpacker");
|
||||||
|
}
|
89
tmate-encoder.c
Normal file
89
tmate-encoder.c
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
|
static int msgpack_write(void *data, const char *buf, unsigned int len)
|
||||||
|
{
|
||||||
|
struct tmate_encoder *encoder = data;
|
||||||
|
|
||||||
|
evbuffer_add(encoder->buffer, buf, len);
|
||||||
|
|
||||||
|
if ((encoder->ev_readable.ev_flags & EVLIST_INSERTED) &&
|
||||||
|
!(encoder->ev_readable.ev_flags & EVLIST_ACTIVE)) {
|
||||||
|
event_active(&encoder->ev_readable, EV_READ, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_encoder_init(struct tmate_encoder *encoder)
|
||||||
|
{
|
||||||
|
msgpack_packer_init(&encoder->pk, encoder, &msgpack_write);
|
||||||
|
encoder->buffer = evbuffer_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define msgpack_pack_string(pk, str) do { \
|
||||||
|
int __strlen = strlen(str); \
|
||||||
|
msgpack_pack_raw(pk, __strlen); \
|
||||||
|
msgpack_pack_raw_body(pk, str, __strlen); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define pack(what, ...) msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__)
|
||||||
|
|
||||||
|
void tmate_write_header(void)
|
||||||
|
{
|
||||||
|
pack(array, 2);
|
||||||
|
pack(int, TMATE_HEADER);
|
||||||
|
pack(int, TMATE_PROTOCOL_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_sync_window(struct window *w)
|
||||||
|
{
|
||||||
|
struct window_pane *wp;
|
||||||
|
int num_panes = 0;
|
||||||
|
int active_pane_id = -1;
|
||||||
|
|
||||||
|
pack(array, 7);
|
||||||
|
pack(int, TMATE_SYNC_WINDOW);
|
||||||
|
|
||||||
|
pack(int, w->id);
|
||||||
|
pack(string, w->name);
|
||||||
|
pack(int, w->sx);
|
||||||
|
pack(int, w->sy);
|
||||||
|
|
||||||
|
TAILQ_FOREACH(wp, &w->panes, entry)
|
||||||
|
num_panes++;
|
||||||
|
|
||||||
|
pack(array, num_panes);
|
||||||
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
|
pack(array, 5);
|
||||||
|
pack(int, wp->id);
|
||||||
|
pack(int, wp->sx);
|
||||||
|
pack(int, wp->sy);
|
||||||
|
pack(int, wp->xoff);
|
||||||
|
pack(int, wp->yoff);
|
||||||
|
|
||||||
|
if (wp == w->active)
|
||||||
|
active_pane_id = wp->id;
|
||||||
|
}
|
||||||
|
pack(int, active_pane_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
size_t max_write, to_write;
|
||||||
|
|
||||||
|
max_write = TMATE_MAX_MESSAGE_SIZE - 4;
|
||||||
|
while (len > 0) {
|
||||||
|
to_write = len < max_write ? len : max_write;
|
||||||
|
|
||||||
|
pack(array, 3);
|
||||||
|
pack(int, TMATE_PTY_DATA);
|
||||||
|
pack(int, wp->id);
|
||||||
|
pack(raw, to_write);
|
||||||
|
pack(raw_body, buf, to_write);
|
||||||
|
|
||||||
|
buf += to_write;
|
||||||
|
len -= to_write;
|
||||||
|
}
|
||||||
|
}
|
301
tmate-ssh-client.c
Normal file
301
tmate-ssh-client.c
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
#include <libssh/libssh.h>
|
||||||
|
#include <libssh/callbacks.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <event.h>
|
||||||
|
|
||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
|
static void consume_channel(struct tmate_ssh_client *client);
|
||||||
|
static void flush_input_stream(struct tmate_ssh_client *client);
|
||||||
|
static void __flush_input_stream(evutil_socket_t fd, short what, void *arg);
|
||||||
|
static void __on_session_event(evutil_socket_t fd, short what, void *arg);
|
||||||
|
static void disconnect_session(struct tmate_ssh_client *client);
|
||||||
|
static void reconnect_session(struct tmate_ssh_client *client);
|
||||||
|
|
||||||
|
static void log_function(ssh_session session, int priority,
|
||||||
|
const char *message, void *userdata)
|
||||||
|
{
|
||||||
|
tmate_debug("[%d] %s", priority, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ssh_callbacks_struct ssh_session_callbacks = {
|
||||||
|
.log_function = log_function
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void register_session_fd_event(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
if (!event_initialized(&client->ev_ssh)) {
|
||||||
|
int flag = 1;
|
||||||
|
setsockopt(ssh_get_fd(client->session), IPPROTO_TCP,
|
||||||
|
TCP_NODELAY, &flag, sizeof(flag));
|
||||||
|
|
||||||
|
event_assign(&client->ev_ssh, ev_base, ssh_get_fd(client->session),
|
||||||
|
EV_READ | EV_PERSIST, __on_session_event, client);
|
||||||
|
event_add(&client->ev_ssh, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_input_stream_event(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
if (!event_initialized(&client->encoder->ev_readable)) {
|
||||||
|
event_assign(&client->encoder->ev_readable, ev_base, -1,
|
||||||
|
EV_READ | EV_PERSIST, __flush_input_stream, client);
|
||||||
|
event_add(&client->encoder->ev_readable, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __ssh_userauth_autopubkey(ssh_session session, const char *passphrase)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* For some reason, auth doesn't work in blocking mode :( */
|
||||||
|
ssh_set_blocking(session, 1);
|
||||||
|
ret = ssh_userauth_autopubkey(session, passphrase);
|
||||||
|
ssh_set_blocking(session, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void consume_channel(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
tmate_decoder_get_buffer(client->decoder, &buf, &len);
|
||||||
|
len = ssh_channel_read_nonblocking(client->channel, buf, len, 0);
|
||||||
|
if (len < 0) {
|
||||||
|
tmate_debug("Error reading from channel: %s",
|
||||||
|
ssh_get_error(client->session));
|
||||||
|
reconnect_session(client);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
tmate_decoder_commit(client->decoder, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_session_event(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
int verbosity = SSH_LOG_RARE;
|
||||||
|
int port = 2200;
|
||||||
|
|
||||||
|
ssh_session session = client->session;
|
||||||
|
ssh_channel channel = client->channel;
|
||||||
|
|
||||||
|
switch (client->state) {
|
||||||
|
case SSH_INIT:
|
||||||
|
client->session = session = ssh_new();
|
||||||
|
if (!session) {
|
||||||
|
tmate_fatal("cannot initialize");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_set_callbacks(session, &ssh_session_callbacks);
|
||||||
|
|
||||||
|
client->channel = channel = ssh_channel_new(session);
|
||||||
|
if (!channel) {
|
||||||
|
tmate_fatal("cannot initialize");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_set_blocking(session, 0);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_USER, "tmate");
|
||||||
|
|
||||||
|
tmate_debug("Connecting...");
|
||||||
|
client->state = SSH_CONNECT;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case SSH_CONNECT:
|
||||||
|
switch (ssh_connect(session)) {
|
||||||
|
case SSH_AGAIN:
|
||||||
|
register_session_fd_event(client);
|
||||||
|
return;
|
||||||
|
case SSH_ERROR:
|
||||||
|
tmate_debug("Error connecting: %s", ssh_get_error(session));
|
||||||
|
reconnect_session(client);
|
||||||
|
return;
|
||||||
|
case SSH_OK:
|
||||||
|
register_session_fd_event(client);
|
||||||
|
tmate_debug("Connected");
|
||||||
|
client->state = SSH_AUTH;
|
||||||
|
/* fall through */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO Authenticate server */
|
||||||
|
|
||||||
|
case SSH_AUTH:
|
||||||
|
switch (__ssh_userauth_autopubkey(session, NULL)) {
|
||||||
|
case SSH_AUTH_AGAIN:
|
||||||
|
return;
|
||||||
|
case SSH_AUTH_PARTIAL:
|
||||||
|
case SSH_AUTH_INFO:
|
||||||
|
case SSH_AUTH_DENIED:
|
||||||
|
tmate_debug("Access denied. Try again later.");
|
||||||
|
disconnect_session(client);
|
||||||
|
return;
|
||||||
|
case SSH_AUTH_ERROR:
|
||||||
|
tmate_debug("Auth error: %s", ssh_get_error(session));
|
||||||
|
reconnect_session(client);
|
||||||
|
return;
|
||||||
|
case SSH_AUTH_SUCCESS:
|
||||||
|
tmate_debug("Auth successful");
|
||||||
|
client->state = SSH_OPEN_CHANNEL;
|
||||||
|
/* fall through */
|
||||||
|
}
|
||||||
|
|
||||||
|
case SSH_OPEN_CHANNEL:
|
||||||
|
switch (ssh_channel_open_session(channel)) {
|
||||||
|
case SSH_AGAIN:
|
||||||
|
return;
|
||||||
|
case SSH_ERROR:
|
||||||
|
tmate_debug("Error opening session: %s", ssh_get_error(session));
|
||||||
|
reconnect_session(client);
|
||||||
|
return;
|
||||||
|
case SSH_OK:
|
||||||
|
tmate_debug("Session opened, initalizing tmate");
|
||||||
|
client->state = SSH_BOOTSTRAP;
|
||||||
|
/* fall through */
|
||||||
|
}
|
||||||
|
|
||||||
|
case SSH_BOOTSTRAP:
|
||||||
|
switch (ssh_channel_request_subsystem(channel, "tmate")) {
|
||||||
|
case SSH_AGAIN:
|
||||||
|
return;
|
||||||
|
case SSH_ERROR:
|
||||||
|
tmate_debug("Error initializing tmate: %s", ssh_get_error(session));
|
||||||
|
reconnect_session(client);
|
||||||
|
return;
|
||||||
|
case SSH_OK:
|
||||||
|
tmate_debug("Ready");
|
||||||
|
|
||||||
|
/* Writes are now performed in a blocking fashion */
|
||||||
|
ssh_set_blocking(session, 1);
|
||||||
|
|
||||||
|
client->state = SSH_READY;
|
||||||
|
register_input_stream_event(client);
|
||||||
|
flush_input_stream(client);
|
||||||
|
/* fall through */
|
||||||
|
}
|
||||||
|
|
||||||
|
case SSH_READY:
|
||||||
|
consume_channel(client);
|
||||||
|
if (!ssh_is_connected(session)) {
|
||||||
|
tmate_debug("Disconnected");
|
||||||
|
reconnect_session(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_input_stream(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
struct evbuffer *evb = client->encoder->buffer;
|
||||||
|
ssize_t len, written;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
if (client->state < SSH_READY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
len = evbuffer_get_length(evb);
|
||||||
|
if (!len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
buf = evbuffer_pullup(evb, -1);
|
||||||
|
|
||||||
|
written = ssh_channel_write(client->channel, buf, len);
|
||||||
|
if (written < 0) {
|
||||||
|
tmate_debug("Error writing to channel: %s",
|
||||||
|
ssh_get_error(client->session));
|
||||||
|
reconnect_session(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
evbuffer_drain(evb, written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __flush_input_stream(evutil_socket_t fd, short what, void *arg)
|
||||||
|
{
|
||||||
|
flush_input_stream(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __on_session_event(evutil_socket_t fd, short what, void *arg)
|
||||||
|
{
|
||||||
|
on_session_event(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disconnect_session(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
if (event_initialized(&client->ev_ssh)) {
|
||||||
|
event_del(&client->ev_ssh);
|
||||||
|
client->ev_ssh.ev_flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_initialized(&client->encoder->ev_readable)) {
|
||||||
|
event_del(&client->encoder->ev_readable);
|
||||||
|
client->encoder->ev_readable.ev_flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->session) {
|
||||||
|
/* ssh_free() also frees the associated channels. */
|
||||||
|
ssh_free(client->session);
|
||||||
|
client->session = NULL;
|
||||||
|
client->channel = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->state = SSH_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void connect_session(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
if (!client->session) {
|
||||||
|
client->state = SSH_INIT;
|
||||||
|
on_session_event(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_reconnect_timer(evutil_socket_t fd, short what, void *arg)
|
||||||
|
{
|
||||||
|
connect_session(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reconnect_session(struct tmate_ssh_client *client)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
disconnect_session(client);
|
||||||
|
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
evtimer_add(&client->ev_ssh_reconnect, &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_ssh_client_init(struct tmate_ssh_client *client,
|
||||||
|
struct tmate_encoder *encoder,
|
||||||
|
struct tmate_decoder *decoder)
|
||||||
|
{
|
||||||
|
ssh_callbacks_init(&ssh_session_callbacks);
|
||||||
|
|
||||||
|
client->state = SSH_NONE;
|
||||||
|
client->session = NULL;
|
||||||
|
client->channel = NULL;
|
||||||
|
|
||||||
|
client->encoder = encoder;
|
||||||
|
client->decoder = decoder;
|
||||||
|
|
||||||
|
evtimer_assign(&client->ev_ssh_reconnect, ev_base,
|
||||||
|
on_reconnect_timer, client);
|
||||||
|
|
||||||
|
connect_session(client);
|
||||||
|
}
|
18
tmate.c
Normal file
18
tmate.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
|
struct tmate_encoder *tmate_encoder;
|
||||||
|
|
||||||
|
static struct tmate_ssh_client client;
|
||||||
|
static struct tmate_encoder encoder;
|
||||||
|
static struct tmate_decoder decoder;
|
||||||
|
|
||||||
|
void tmate_client_start(void)
|
||||||
|
{
|
||||||
|
tmate_encoder_init(&encoder);
|
||||||
|
tmate_decoder_init(&decoder);
|
||||||
|
tmate_encoder = &encoder;
|
||||||
|
|
||||||
|
tmate_ssh_client_init(&client, &encoder, &decoder);
|
||||||
|
|
||||||
|
tmate_write_header();
|
||||||
|
}
|
94
tmate.h
Normal file
94
tmate.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#ifndef TMATE_H
|
||||||
|
#define TMATE_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <msgpack.h>
|
||||||
|
#include <event.h>
|
||||||
|
|
||||||
|
#include "tmux.h"
|
||||||
|
|
||||||
|
#define tmate_debug(...) log_debug("[tmate] " __VA_ARGS__)
|
||||||
|
#define tmate_warn(...) log_warn("[tmate] " __VA_ARGS__)
|
||||||
|
#define tmate_info(...) log_info("[tmate] " __VA_ARGS__)
|
||||||
|
#define tmate_fatal(...) log_fatal("[tmate] " __VA_ARGS__)
|
||||||
|
|
||||||
|
/* tmate-encoder.c */
|
||||||
|
|
||||||
|
#define TMATE_MAX_MESSAGE_SIZE (16*1024)
|
||||||
|
|
||||||
|
#define TMATE_PROTOCOL_VERSION 1
|
||||||
|
|
||||||
|
enum tmate_commands {
|
||||||
|
TMATE_HEADER,
|
||||||
|
TMATE_SYNC_WINDOW,
|
||||||
|
TMATE_PTY_DATA,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tmate_encoder {
|
||||||
|
msgpack_packer pk;
|
||||||
|
struct evbuffer *buffer;
|
||||||
|
struct event ev_readable;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void tmate_encoder_init(struct tmate_encoder *encoder);
|
||||||
|
|
||||||
|
extern void tmate_write_header(void);
|
||||||
|
extern void tmate_sync_window(struct window *w);
|
||||||
|
extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len);
|
||||||
|
|
||||||
|
/* tmate-decoder.c */
|
||||||
|
|
||||||
|
enum tmate_notifications {
|
||||||
|
TMATE_CLIENT_KEY,
|
||||||
|
TMATE_CLIENT_RESIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tmate_decoder {
|
||||||
|
struct msgpack_unpacker unpacker;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int tmate_sx;
|
||||||
|
extern int tmate_sy;
|
||||||
|
|
||||||
|
extern void tmate_decoder_init(struct tmate_decoder *decoder);
|
||||||
|
extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder,
|
||||||
|
char **buf, size_t *len);
|
||||||
|
extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len);
|
||||||
|
|
||||||
|
/* tmate-ssh-client.c */
|
||||||
|
|
||||||
|
typedef struct ssh_session_struct* ssh_session;
|
||||||
|
typedef struct ssh_channel_struct* ssh_channel;
|
||||||
|
|
||||||
|
enum tmate_ssh_client_state_types {
|
||||||
|
SSH_NONE,
|
||||||
|
SSH_INIT,
|
||||||
|
SSH_CONNECT,
|
||||||
|
SSH_AUTH,
|
||||||
|
SSH_OPEN_CHANNEL,
|
||||||
|
SSH_BOOTSTRAP,
|
||||||
|
SSH_READY,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tmate_ssh_client {
|
||||||
|
int state;
|
||||||
|
ssh_session session;
|
||||||
|
ssh_channel channel;
|
||||||
|
|
||||||
|
struct tmate_encoder *encoder;
|
||||||
|
struct tmate_decoder *decoder;
|
||||||
|
|
||||||
|
struct event ev_ssh;
|
||||||
|
struct event ev_ssh_reconnect;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void tmate_ssh_client_init(struct tmate_ssh_client *client,
|
||||||
|
struct tmate_encoder *encoder,
|
||||||
|
struct tmate_decoder *decoder);
|
||||||
|
|
||||||
|
/* tmate.c */
|
||||||
|
|
||||||
|
extern struct tmate_encoder *tmate_encoder;
|
||||||
|
extern void tmate_client_start(void);
|
||||||
|
|
||||||
|
#endif
|
7
tmux.c
7
tmux.c
@ -168,9 +168,9 @@ makesocketpath(const char *label)
|
|||||||
|
|
||||||
uid = getuid();
|
uid = getuid();
|
||||||
if ((s = getenv("TMPDIR")) == NULL || *s == '\0')
|
if ((s = getenv("TMPDIR")) == NULL || *s == '\0')
|
||||||
xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid);
|
xsnprintf(base, sizeof base, "%s/tmate-%u", _PATH_TMP, uid);
|
||||||
else
|
else
|
||||||
xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid);
|
xsnprintf(base, sizeof base, "%s/tmate-%u", s, uid);
|
||||||
|
|
||||||
if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
|
if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -244,7 +244,8 @@ main(int argc, char **argv)
|
|||||||
malloc_options = (char *) "AFGJPX";
|
malloc_options = (char *) "AFGJPX";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
quiet = flags = 0;
|
flags = IDENTIFY_256COLOURS | IDENTIFY_UTF8;
|
||||||
|
quiet = 0;
|
||||||
label = path = NULL;
|
label = path = NULL;
|
||||||
login_shell = (**argv == '-');
|
login_shell = (**argv == '-');
|
||||||
while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUvV")) != -1) {
|
while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUvV")) != -1) {
|
||||||
|
4
tmux.h
4
tmux.h
@ -19,6 +19,8 @@
|
|||||||
#ifndef TMUX_H
|
#ifndef TMUX_H
|
||||||
#define TMUX_H
|
#define TMUX_H
|
||||||
|
|
||||||
|
#define TMATE
|
||||||
|
|
||||||
#define PROTOCOL_VERSION 7
|
#define PROTOCOL_VERSION 7
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
@ -959,6 +961,8 @@ struct window_pane {
|
|||||||
struct bufferevent *pipe_event;
|
struct bufferevent *pipe_event;
|
||||||
size_t pipe_off;
|
size_t pipe_off;
|
||||||
|
|
||||||
|
size_t tmate_off;
|
||||||
|
|
||||||
struct screen *screen;
|
struct screen *screen;
|
||||||
struct screen base;
|
struct screen base;
|
||||||
|
|
||||||
|
17
window.c
17
window.c
@ -30,6 +30,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
#include "tmate.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each window is attached to a number of panes, each of which is a pty. This
|
* Each window is attached to a number of panes, each of which is a pty. This
|
||||||
@ -328,6 +329,8 @@ window_create(const char *name, const char *cmd, const char *shell,
|
|||||||
} else
|
} else
|
||||||
w->name = default_window_name(w);
|
w->name = default_window_name(w);
|
||||||
|
|
||||||
|
tmate_sync_window(w);
|
||||||
|
|
||||||
return (w);
|
return (w);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +377,7 @@ window_set_name(struct window *w, const char *new_name)
|
|||||||
free(w->name);
|
free(w->name);
|
||||||
w->name = xstrdup(new_name);
|
w->name = xstrdup(new_name);
|
||||||
notify_window_renamed(w);
|
notify_window_renamed(w);
|
||||||
|
tmate_sync_window(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -691,6 +695,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
|
|||||||
wp->pipe_off = 0;
|
wp->pipe_off = 0;
|
||||||
wp->pipe_event = NULL;
|
wp->pipe_event = NULL;
|
||||||
|
|
||||||
|
wp->tmate_off = 0;
|
||||||
|
|
||||||
wp->saved_grid = NULL;
|
wp->saved_grid = NULL;
|
||||||
|
|
||||||
screen_init(&wp->base, sx, sy, hlimit);
|
screen_init(&wp->base, sx, sy, hlimit);
|
||||||
@ -872,13 +878,24 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data)
|
|||||||
|
|
||||||
new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off;
|
new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off;
|
||||||
if (wp->pipe_fd != -1 && new_size > 0) {
|
if (wp->pipe_fd != -1 && new_size > 0) {
|
||||||
|
/* FIXME tmux:
|
||||||
|
* - new_data = EVBUFFER_DATA(wp->event->input);
|
||||||
|
* + new_data = EVBUFFER_DATA(wp->event->input) + wp->pipe_off;
|
||||||
|
* also, can the buffer be too small?
|
||||||
|
*/
|
||||||
new_data = EVBUFFER_DATA(wp->event->input);
|
new_data = EVBUFFER_DATA(wp->event->input);
|
||||||
bufferevent_write(wp->pipe_event, new_data, new_size);
|
bufferevent_write(wp->pipe_event, new_data, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_size = EVBUFFER_LENGTH(wp->event->input) - wp->tmate_off;
|
||||||
|
new_data = EVBUFFER_DATA(wp->event->input) + wp->tmate_off;
|
||||||
|
if (new_size > 0)
|
||||||
|
tmate_pty_data(wp, new_data, new_size);
|
||||||
|
|
||||||
input_parse(wp);
|
input_parse(wp);
|
||||||
|
|
||||||
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
|
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
|
||||||
|
wp->tmate_off = EVBUFFER_LENGTH(wp->event->input);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we get here, we're not outputting anymore, so set the silence
|
* If we get here, we're not outputting anymore, so set the silence
|
||||||
|
Loading…
x
Reference in New Issue
Block a user