mirror of
https://github.com/tmate-io/tmate.git
synced 2025-01-11 08:28:29 +01:00
Reusing protocol code from tmate-slave
This commit is contained in:
parent
a60da0db1e
commit
a87d3af309
@ -177,6 +177,7 @@ dist_tmate_SOURCES = \
|
|||||||
tmate-decoder.c \
|
tmate-decoder.c \
|
||||||
tmate-env.c \
|
tmate-env.c \
|
||||||
tmate-msg.c \
|
tmate-msg.c \
|
||||||
|
tmate-msgpack.c \
|
||||||
tmate-session.c \
|
tmate-session.c \
|
||||||
tmux.c \
|
tmux.c \
|
||||||
tty-acs.c \
|
tty-acs.c \
|
||||||
|
@ -193,7 +193,7 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
|
|||||||
struct client *c = cmdq->client;
|
struct client *c = cmdq->client;
|
||||||
|
|
||||||
#ifdef TMATE
|
#ifdef TMATE
|
||||||
if (!strcmp(name, "tmate-ready") && tmate_session.decoder.ready)
|
if (!strcmp(name, "tmate-ready") && tmate_session.tmate_env_ready)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
4
resize.c
4
resize.c
@ -52,6 +52,10 @@ recalculate_sizes(void)
|
|||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
u_int ssx, ssy, has, limit;
|
u_int ssx, ssy, has, limit;
|
||||||
int flag, has_status, is_zoomed, forced;
|
int flag, has_status, is_zoomed, forced;
|
||||||
|
#ifdef TMATE
|
||||||
|
int tmate_sx = tmate_session.min_sx;
|
||||||
|
int tmate_sy = tmate_session.min_sy;
|
||||||
|
#endif
|
||||||
|
|
||||||
RB_FOREACH(s, sessions, &sessions) {
|
RB_FOREACH(s, sessions, &sessions) {
|
||||||
has_status = options_get_number(s->options, "status");
|
has_status = options_get_number(s->options, "status");
|
||||||
|
2
server.c
2
server.c
@ -132,7 +132,7 @@ server_create_socket(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TMATE
|
#ifdef TMATE
|
||||||
void tmate_set_editor_mode(void)
|
static void tmate_set_editor_mode(void)
|
||||||
{
|
{
|
||||||
switch (options_get_number(global_s_options, "status-keys")) {
|
switch (options_get_number(global_s_options, "status-keys")) {
|
||||||
case MODEKEY_EMACS: tmate_exec_cmd("set-option -g status-keys emacs"); break;
|
case MODEKEY_EMACS: tmate_exec_cmd("set-option -g status-keys emacs"); break;
|
||||||
|
@ -53,7 +53,7 @@ static int print_resolved_stack_frame(const char *frame)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void tmate_print_trace(void)
|
void tmate_print_stack_trace(void)
|
||||||
{
|
{
|
||||||
void *array[20];
|
void *array[20];
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -80,7 +80,7 @@ static void handle_sigsegv(__unused int sig)
|
|||||||
{
|
{
|
||||||
/* TODO send stack trace to server */
|
/* TODO send stack trace to server */
|
||||||
tmate_info("CRASH, printing stack trace");
|
tmate_info("CRASH, printing stack trace");
|
||||||
tmate_print_trace();
|
tmate_print_stack_trace();
|
||||||
tmate_fatal("CRASHED");
|
tmate_fatal("CRASHED");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
173
tmate-decoder.c
173
tmate-decoder.c
@ -1,88 +1,16 @@
|
|||||||
#include "tmate.h"
|
#include "tmate.h"
|
||||||
|
#include "tmate-protocol.h"
|
||||||
|
|
||||||
int tmate_sx = -1;
|
static void handle_notify(__unused struct tmate_session *session,
|
||||||
int tmate_sy = -1;
|
struct tmate_unpacker *uk)
|
||||||
|
|
||||||
struct tmate_unpacker {
|
|
||||||
msgpack_object *argv;
|
|
||||||
int argc;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void decoder_error(void)
|
|
||||||
{
|
|
||||||
/* TODO Don't kill the session, disconnect */
|
|
||||||
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 unpack_raw(struct tmate_unpacker *uk,
|
|
||||||
const char **buf, size_t *len)
|
|
||||||
{
|
|
||||||
if (uk->argc == 0)
|
|
||||||
decoder_error();
|
|
||||||
|
|
||||||
if (uk->argv[0].type != MSGPACK_OBJECT_STR &&
|
|
||||||
uk->argv[0].type != MSGPACK_OBJECT_BIN)
|
|
||||||
decoder_error();
|
|
||||||
|
|
||||||
*len = uk->argv[0].via.str.size;
|
|
||||||
*buf = uk->argv[0].via.str.ptr;
|
|
||||||
|
|
||||||
uk->argv++;
|
|
||||||
uk->argc--;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *unpack_string(struct tmate_unpacker *uk)
|
|
||||||
{
|
|
||||||
const char *buf;
|
|
||||||
char *alloc_buf;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
unpack_raw(uk, &buf, &len);
|
|
||||||
|
|
||||||
alloc_buf = xmalloc(len + 1);
|
|
||||||
memcpy(alloc_buf, buf, len);
|
|
||||||
alloc_buf[len] = '\0';
|
|
||||||
|
|
||||||
return alloc_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tmate_notify(struct tmate_unpacker *uk)
|
|
||||||
{
|
{
|
||||||
char *msg = unpack_string(uk);
|
char *msg = unpack_string(uk);
|
||||||
tmate_status_message("%s", msg);
|
tmate_status_message("%s", msg);
|
||||||
free(msg);
|
free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tmate_client_pane_key(struct tmate_unpacker *uk)
|
static void handle_legacy_pane_key(__unused struct tmate_session *_session,
|
||||||
|
struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
struct session *s;
|
struct session *s;
|
||||||
struct window *w;
|
struct window *w;
|
||||||
@ -135,7 +63,8 @@ slow_path:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tmate_client_pane_tmux_key(struct tmate_unpacker *uk)
|
static void handle_pane_key(__unused struct tmate_session *_session,
|
||||||
|
struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
struct session *s;
|
struct session *s;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
@ -154,20 +83,19 @@ static void tmate_client_pane_tmux_key(struct tmate_unpacker *uk)
|
|||||||
window_pane_key(wp, NULL, s, key, NULL);
|
window_pane_key(wp, NULL, s, key, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tmate_client_resize(struct tmate_unpacker *uk)
|
static void handle_resize(struct tmate_session *session,
|
||||||
|
struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
/* TODO This is sad, we might want our own client. */
|
session->min_sx = unpack_int(uk);
|
||||||
tmate_sx = unpack_int(uk);
|
session->min_sy = unpack_int(uk);
|
||||||
tmate_sy = unpack_int(uk);
|
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
|
|
||||||
/* TODO Handle reconnection cases */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern char **cfg_causes;
|
extern char **cfg_causes;
|
||||||
extern u_int cfg_ncauses;
|
extern u_int cfg_ncauses;
|
||||||
|
|
||||||
static void tmate_client_exec_cmd(struct tmate_unpacker *uk)
|
static void handle_exec_cmd(__unused struct tmate_session *session,
|
||||||
|
struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
struct cmd_q *cmd_q;
|
struct cmd_q *cmd_q;
|
||||||
struct cmd_list *cmdlist;
|
struct cmd_list *cmdlist;
|
||||||
@ -202,7 +130,8 @@ out:
|
|||||||
free(cmd_str);
|
free(cmd_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tmate_client_env(struct tmate_unpacker *uk)
|
static void handle_set_env(__unused struct tmate_session *session,
|
||||||
|
struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
char *name = unpack_string(uk);
|
char *name = unpack_string(uk);
|
||||||
char *value = unpack_string(uk);
|
char *value = unpack_string(uk);
|
||||||
@ -213,68 +142,26 @@ static void tmate_client_env(struct tmate_unpacker *uk)
|
|||||||
free(value);
|
free(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_ready(__unused struct tmate_session *session,
|
||||||
extern void signal_waiting_clients(const char *name);
|
|
||||||
static void tmate_client_ready(struct tmate_decoder *decoder,
|
|
||||||
__unused struct tmate_unpacker *uk)
|
__unused struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
decoder->ready = 1;
|
session->tmate_env_ready = 1;
|
||||||
signal_waiting_clients("tmate-ready");
|
signal_waiting_clients("tmate-ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_message(struct tmate_decoder *decoder, msgpack_object obj)
|
void tmate_dispatch_slave_message(struct tmate_session *session,
|
||||||
|
struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
struct tmate_unpacker _uk;
|
int cmd = unpack_int(uk);
|
||||||
struct tmate_unpacker *uk = &_uk;
|
switch (cmd) {
|
||||||
|
#define dispatch(c, f) case c: f(session, uk); break
|
||||||
init_unpacker(uk, obj);
|
dispatch(TMATE_IN_NOTIFY, handle_notify);
|
||||||
|
dispatch(TMATE_IN_LEGACY_PANE_KEY, handle_legacy_pane_key);
|
||||||
switch (unpack_int(uk)) {
|
dispatch(TMATE_IN_RESIZE, handle_resize);
|
||||||
case TMATE_NOTIFY: tmate_notify(uk); break;
|
dispatch(TMATE_IN_EXEC_CMD, handle_exec_cmd);
|
||||||
case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break;
|
dispatch(TMATE_IN_SET_ENV, handle_set_env);
|
||||||
case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break;
|
dispatch(TMATE_IN_READY, handle_ready);
|
||||||
case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break;
|
dispatch(TMATE_IN_PANE_KEY, handle_pane_key);
|
||||||
case TMATE_CLIENT_ENV: tmate_client_env(uk); break;
|
default: tmate_fatal("Bad message type: %d", cmd);
|
||||||
case TMATE_CLIENT_READY: tmate_client_ready(decoder, uk); break;
|
|
||||||
case TMATE_CLIENT_PANE_TMUX_KEY: tmate_client_pane_tmux_key(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(decoder, 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");
|
|
||||||
decoder->ready = 0;
|
|
||||||
}
|
|
||||||
|
@ -1,41 +1,13 @@
|
|||||||
#include "tmate.h"
|
#include "tmate.h"
|
||||||
|
#include "tmate-protocol.h"
|
||||||
#include "window-copy.h"
|
#include "window-copy.h"
|
||||||
|
|
||||||
#define DEFAULT_ENCODER (&tmate_session.encoder)
|
#define pack(what, ...) _pack(&tmate_session.encoder, what, __VA_ARGS__)
|
||||||
|
|
||||||
static int msgpack_write(void *data, const char *buf, size_t 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->ev_readable.ev_flags = 0;
|
|
||||||
encoder->buffer = evbuffer_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define msgpack_pack_string(pk, str) do { \
|
|
||||||
int __strlen = strlen(str); \
|
|
||||||
msgpack_pack_str(pk, __strlen); \
|
|
||||||
msgpack_pack_str_body(pk, str, __strlen); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define pack(what, ...) msgpack_pack_##what(&DEFAULT_ENCODER->pk, __VA_ARGS__)
|
|
||||||
|
|
||||||
void tmate_write_header(void)
|
void tmate_write_header(void)
|
||||||
{
|
{
|
||||||
pack(array, 3);
|
pack(array, 3);
|
||||||
pack(int, TMATE_HEADER);
|
pack(int, TMATE_OUT_HEADER);
|
||||||
pack(int, TMATE_PROTOCOL_VERSION);
|
pack(int, TMATE_PROTOCOL_VERSION);
|
||||||
pack(string, VERSION);
|
pack(string, VERSION);
|
||||||
}
|
}
|
||||||
@ -79,7 +51,7 @@ void tmate_sync_layout(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
pack(array, 5);
|
pack(array, 5);
|
||||||
pack(int, TMATE_SYNC_LAYOUT);
|
pack(int, TMATE_OUT_SYNC_LAYOUT);
|
||||||
|
|
||||||
pack(int, s->sx);
|
pack(int, s->sx);
|
||||||
pack(int, s->sy);
|
pack(int, s->sy);
|
||||||
@ -134,12 +106,12 @@ void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len)
|
|||||||
{
|
{
|
||||||
size_t max_write, to_write;
|
size_t max_write, to_write;
|
||||||
|
|
||||||
max_write = TMATE_MAX_MESSAGE_SIZE - 4;
|
max_write = TMATE_MAX_MESSAGE_SIZE - 16;
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
to_write = len < max_write ? len : max_write;
|
to_write = len < max_write ? len : max_write;
|
||||||
|
|
||||||
pack(array, 3);
|
pack(array, 3);
|
||||||
pack(int, TMATE_PTY_DATA);
|
pack(int, TMATE_OUT_PTY_DATA);
|
||||||
pack(int, wp->id);
|
pack(int, wp->id);
|
||||||
pack(str, to_write);
|
pack(str, to_write);
|
||||||
pack(str_body, buf, to_write);
|
pack(str_body, buf, to_write);
|
||||||
@ -175,14 +147,14 @@ int tmate_should_replicate_cmd(const struct cmd_entry *cmd)
|
|||||||
void tmate_exec_cmd(const char *cmd)
|
void tmate_exec_cmd(const char *cmd)
|
||||||
{
|
{
|
||||||
pack(array, 2);
|
pack(array, 2);
|
||||||
pack(int, TMATE_EXEC_CMD);
|
pack(int, TMATE_OUT_EXEC_CMD);
|
||||||
pack(string, cmd);
|
pack(string, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tmate_failed_cmd(int client_id, const char *cause)
|
void tmate_failed_cmd(int client_id, const char *cause)
|
||||||
{
|
{
|
||||||
pack(array, 3);
|
pack(array, 3);
|
||||||
pack(int, TMATE_FAILED_CMD);
|
pack(int, TMATE_OUT_FAILED_CMD);
|
||||||
pack(int, client_id);
|
pack(int, client_id);
|
||||||
pack(string, cause);
|
pack(string, cause);
|
||||||
}
|
}
|
||||||
@ -196,7 +168,7 @@ void tmate_status(const char *left, const char *right)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
pack(array, 3);
|
pack(array, 3);
|
||||||
pack(int, TMATE_STATUS);
|
pack(int, TMATE_OUT_STATUS);
|
||||||
pack(string, left);
|
pack(string, left);
|
||||||
pack(string, right);
|
pack(string, right);
|
||||||
|
|
||||||
@ -211,7 +183,7 @@ void tmate_sync_copy_mode(struct window_pane *wp)
|
|||||||
struct window_copy_mode_data *data = wp->modedata;
|
struct window_copy_mode_data *data = wp->modedata;
|
||||||
|
|
||||||
pack(array, 3);
|
pack(array, 3);
|
||||||
pack(int, TMATE_SYNC_COPY_MODE);
|
pack(int, TMATE_OUT_SYNC_COPY_MODE);
|
||||||
|
|
||||||
pack(int, wp->id);
|
pack(int, wp->id);
|
||||||
|
|
||||||
@ -248,7 +220,7 @@ void tmate_sync_copy_mode(struct window_pane *wp)
|
|||||||
void tmate_write_copy_mode(struct window_pane *wp, const char *str)
|
void tmate_write_copy_mode(struct window_pane *wp, const char *str)
|
||||||
{
|
{
|
||||||
pack(array, 3);
|
pack(array, 3);
|
||||||
pack(int, TMATE_WRITE_COPY_MODE);
|
pack(int, TMATE_OUT_WRITE_COPY_MODE);
|
||||||
pack(int, wp->id);
|
pack(int, wp->id);
|
||||||
pack(string, str);
|
pack(string, str);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ static void tmate_status_message_client(struct client *c, const char *message)
|
|||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
struct message_entry *msg, *msg1;
|
struct message_entry *msg, *msg1;
|
||||||
int delay;
|
int delay;
|
||||||
u_int first, limit;
|
u_int limit;
|
||||||
|
|
||||||
limit = options_get_number(global_options, "message-limit");
|
limit = options_get_number(global_options, "message-limit");
|
||||||
delay = options_get_number(c->session ? c->session->options : global_s_options,
|
delay = options_get_number(c->session ? c->session->options : global_s_options,
|
||||||
|
206
tmate-msgpack.c
Normal file
206
tmate-msgpack.c
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
#include "tmate.h"
|
||||||
|
#include "tmate-protocol.h"
|
||||||
|
|
||||||
|
static void on_encoder_buffer_ready(__unused evutil_socket_t fd,
|
||||||
|
__unused short what, void *arg)
|
||||||
|
{
|
||||||
|
struct tmate_encoder *encoder = arg;
|
||||||
|
|
||||||
|
encoder->ev_active = false;
|
||||||
|
if (encoder->ready_callback)
|
||||||
|
encoder->ready_callback(encoder->userdata, encoder->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_encoder_write(void *userdata, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct tmate_encoder *encoder = userdata;
|
||||||
|
|
||||||
|
if (evbuffer_add(encoder->buffer, buf, len) < 0)
|
||||||
|
tmate_fatal("Cannot buffer encoded data");
|
||||||
|
|
||||||
|
if (!encoder->ev_active) {
|
||||||
|
event_active(&encoder->ev_buffer, EV_READ, 0);
|
||||||
|
encoder->ev_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Really sad hack, but we can get away with it */
|
||||||
|
#define tmate_encoder_from_pk(pk) ((struct tmate_encoder *)pk)
|
||||||
|
|
||||||
|
void msgpack_pack_string(msgpack_packer *pk, const char *str)
|
||||||
|
{
|
||||||
|
size_t len = strlen(str);
|
||||||
|
|
||||||
|
msgpack_pack_str(pk, len);
|
||||||
|
msgpack_pack_str_body(pk, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void msgpack_pack_boolean(msgpack_packer *pk, bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
msgpack_pack_true(pk);
|
||||||
|
else
|
||||||
|
msgpack_pack_false(pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_encoder_init(struct tmate_encoder *encoder,
|
||||||
|
tmate_encoder_write_cb *callback,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
msgpack_packer_init(&encoder->pk, encoder, &on_encoder_write);
|
||||||
|
encoder->buffer = evbuffer_new();
|
||||||
|
encoder->ready_callback = callback;
|
||||||
|
encoder->userdata = userdata;
|
||||||
|
|
||||||
|
if (!encoder->buffer)
|
||||||
|
tmate_fatal("Can't allocate buffer");
|
||||||
|
|
||||||
|
event_set(&encoder->ev_buffer, -1,
|
||||||
|
EV_READ | EV_PERSIST, on_encoder_buffer_ready, encoder);
|
||||||
|
|
||||||
|
event_add(&encoder->ev_buffer, NULL);
|
||||||
|
|
||||||
|
encoder->ev_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder,
|
||||||
|
tmate_encoder_write_cb *callback,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
encoder->ready_callback = callback;
|
||||||
|
encoder->userdata = userdata;
|
||||||
|
encoder->ready_callback(encoder->userdata, encoder->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_decoder_error(void)
|
||||||
|
{
|
||||||
|
/* TODO Don't kill the session, disconnect */
|
||||||
|
tmate_print_stack_trace();
|
||||||
|
tmate_fatal("Received a bad message");
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj)
|
||||||
|
{
|
||||||
|
if (obj.type != MSGPACK_OBJECT_ARRAY)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
uk->argv = obj.via.array.ptr;
|
||||||
|
uk->argc = obj.via.array.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t unpack_int(struct tmate_unpacker *uk)
|
||||||
|
{
|
||||||
|
int64_t val;
|
||||||
|
|
||||||
|
if (uk->argc == 0)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER &&
|
||||||
|
uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
val = uk->argv[0].via.i64;
|
||||||
|
|
||||||
|
uk->argv++;
|
||||||
|
uk->argc--;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unpack_bool(struct tmate_unpacker *uk)
|
||||||
|
{
|
||||||
|
bool val;
|
||||||
|
|
||||||
|
if (uk->argc == 0)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
if (uk->argv[0].type != MSGPACK_OBJECT_BOOLEAN)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
val = uk->argv[0].via.boolean;
|
||||||
|
|
||||||
|
uk->argv++;
|
||||||
|
uk->argc--;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len)
|
||||||
|
{
|
||||||
|
if (uk->argc == 0)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
if (uk->argv[0].type != MSGPACK_OBJECT_STR &&
|
||||||
|
uk->argv[0].type != MSGPACK_OBJECT_BIN)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
*len = uk->argv[0].via.str.size;
|
||||||
|
*buf = uk->argv[0].via.str.ptr;
|
||||||
|
|
||||||
|
uk->argv++;
|
||||||
|
uk->argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *unpack_string(struct tmate_unpacker *uk)
|
||||||
|
{
|
||||||
|
const char *buf;
|
||||||
|
char *alloc_buf;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
unpack_buffer(uk, &buf, &len);
|
||||||
|
|
||||||
|
alloc_buf = xmalloc(len + 1);
|
||||||
|
memcpy(alloc_buf, buf, len);
|
||||||
|
alloc_buf[len] = '\0';
|
||||||
|
|
||||||
|
return alloc_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested)
|
||||||
|
{
|
||||||
|
if (uk->argc == 0)
|
||||||
|
tmate_decoder_error();
|
||||||
|
|
||||||
|
init_unpacker(nested, uk->argv[0]);
|
||||||
|
|
||||||
|
uk->argv++;
|
||||||
|
uk->argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
if (!msgpack_unpacker_init(&decoder->unpacker, TMATE_MAX_MESSAGE_SIZE))
|
||||||
|
tmate_fatal("Cannot initialize the unpacker");
|
||||||
|
decoder->reader = reader;
|
||||||
|
decoder->userdata = userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_decoder_get_buffer(struct tmate_decoder *decoder,
|
||||||
|
char **buf, size_t *len)
|
||||||
|
{
|
||||||
|
ssize_t current_size = msgpack_unpacker_message_size(&decoder->unpacker);
|
||||||
|
if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker,
|
||||||
|
TMATE_MAX_MESSAGE_SIZE - current_size))
|
||||||
|
tmate_fatal("cannot expand decoder buffer");
|
||||||
|
|
||||||
|
*buf = msgpack_unpacker_buffer(&decoder->unpacker);
|
||||||
|
*len = msgpack_unpacker_buffer_capacity(&decoder->unpacker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len)
|
||||||
|
{
|
||||||
|
struct tmate_unpacker _uk, *uk = &_uk;
|
||||||
|
msgpack_unpacked result;
|
||||||
|
|
||||||
|
msgpack_unpacker_buffer_consumed(&decoder->unpacker, len);
|
||||||
|
|
||||||
|
msgpack_unpacked_init(&result);
|
||||||
|
while (msgpack_unpacker_next(&decoder->unpacker, &result)) {
|
||||||
|
init_unpacker(uk, result.data);
|
||||||
|
decoder->reader(decoder->userdata, uk);
|
||||||
|
}
|
||||||
|
msgpack_unpacked_destroy(&result);
|
||||||
|
}
|
89
tmate-protocol.h
Normal file
89
tmate-protocol.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#ifndef TMATE_PROTOCOL_H
|
||||||
|
#define TMATE_PROTOCOL_H
|
||||||
|
|
||||||
|
#define TMATE_MAX_MESSAGE_SIZE (16*1024)
|
||||||
|
|
||||||
|
enum tmate_control_out_msg_types {
|
||||||
|
TMATE_CTL_AUTH,
|
||||||
|
TMATE_CTL_DEAMON_OUT_MSG,
|
||||||
|
TMATE_CTL_SNAPSHOT,
|
||||||
|
TMATE_CTL_CLIENT_JOIN,
|
||||||
|
TMATE_CTL_CLIENT_LEFT,
|
||||||
|
TMATE_CTL_EXEC,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
[TMATE_CTL_AUTH, int: ctl_proto_version, string: ip_address, string: pubkey,
|
||||||
|
string: session_token, string: session_token_ro]
|
||||||
|
[TMATE_CTL_DEAMON_OUT_MSG, object: msg]
|
||||||
|
[TMATE_CTL_SNAPSHOT, [[int: pane_id, [int: cur_x, int: cur_y], int: mode,
|
||||||
|
[[string: line_utf8, [int: char_attr, ...]], ...], ...], ...]]
|
||||||
|
[TMATE_CTL_CLIENT_JOIN, int: client_id, string: ip_address, string: pubkey, boolean: readonly]
|
||||||
|
[TMATE_CTL_CLIENT_LEFT, int: client_id]
|
||||||
|
[TMATE_CTL_EXEC, string: username, string: ip_address, string: pubkey, string: command]
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum tmate_control_in_msg_types {
|
||||||
|
TMATE_CTL_DEAMON_FWD_MSG,
|
||||||
|
TMATE_CTL_REQUEST_SNAPSHOT,
|
||||||
|
TMATE_CTL_PANE_KEYS,
|
||||||
|
TMATE_CTL_RESIZE,
|
||||||
|
TMATE_CTL_EXEC_RESPONSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
[TMATE_CTL_DEAMON_FWD_MSG, object: msg]
|
||||||
|
[TMATE_CTL_REQUEST_SNAPSHOT, int: max_history_lines]
|
||||||
|
[TMATE_CTL_PANE_KEYS, int: pane_id, string: keys]
|
||||||
|
[TMATE_CTL_RESIZE, int: sx, int: sy] // sx == -1: no clients
|
||||||
|
[TMATE_CTL_EXEC_RESPONSE, int: exit_code, string: message]
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum tmate_daemon_out_msg_types {
|
||||||
|
TMATE_OUT_HEADER,
|
||||||
|
TMATE_OUT_SYNC_LAYOUT,
|
||||||
|
TMATE_OUT_PTY_DATA,
|
||||||
|
TMATE_OUT_EXEC_CMD,
|
||||||
|
TMATE_OUT_FAILED_CMD,
|
||||||
|
TMATE_OUT_STATUS,
|
||||||
|
TMATE_OUT_SYNC_COPY_MODE,
|
||||||
|
TMATE_OUT_WRITE_COPY_MODE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
[TMATE_OUT_HEADER, int: proto_version, string: version]
|
||||||
|
[TMATE_OUT_SYNC_LAYOUT, [int: sx, int: sy, [[int: win_id, string: win_name,
|
||||||
|
[[int: pane_id, int: sx, int: sy, int: xoff, int: yoff], ...],
|
||||||
|
int: active_pane_id], ...], int: active_win_id]
|
||||||
|
[TMATE_OUT_PTY_DATA, int: pane_id, binary: buffer]
|
||||||
|
[TMATE_OUT_EXEC_CMD, string: cmd]
|
||||||
|
[TMATE_OUT_FAILED_CMD, int: client_id, string: cause]
|
||||||
|
[TMATE_OUT_STATUS, string: left, string: right]
|
||||||
|
[TMATE_OUT_SYNC_COPY_MODE, int: pane_id, [int: backing, int: oy, int: cx, int: cy,
|
||||||
|
[int: selx, int: sely, int: flags],
|
||||||
|
[int: type, string: input_prompt, string: input_str]])
|
||||||
|
// Any of the array can be []
|
||||||
|
[TMATE_OUT_WRITE_COPY_MODE, int: pane_id, string: str]
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum tmate_daemon_in_msg_types {
|
||||||
|
TMATE_IN_NOTIFY,
|
||||||
|
TMATE_IN_LEGACY_PANE_KEY,
|
||||||
|
TMATE_IN_RESIZE,
|
||||||
|
TMATE_IN_EXEC_CMD,
|
||||||
|
TMATE_IN_SET_ENV,
|
||||||
|
TMATE_IN_READY,
|
||||||
|
TMATE_IN_PANE_KEY,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
[TMATE_IN_NOTIFY, string: msg]
|
||||||
|
[TMATE_IN_PANE_KEY, int: key]
|
||||||
|
[TMATE_IN_RESIZE, int: sx, int: sy] // sx == -1: no clients
|
||||||
|
[TMATE_IN_EXEC_CMD, int: client_id, string: cmd]
|
||||||
|
[TMATE_IN_SET_ENV, string: name, string: value]
|
||||||
|
[TMATE_IN_READY]
|
||||||
|
[TMATE_IN_PANE_KEY, int: pane_id, uint64 keycode] // pane_id == -1: active pane
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
@ -99,27 +99,31 @@ static void lookup_and_connect(void)
|
|||||||
&hints, dns_cb, (void *)tmate_server_host);
|
&hints, dns_cb, (void *)tmate_server_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssh_log_function(int priority, const char *function,
|
static void __tmate_session_init(struct tmate_session *session,
|
||||||
const char *buffer, __unused void *userdata)
|
struct event_base *base)
|
||||||
{
|
{
|
||||||
tmate_debug("[%d] [%s] %s", priority, function, buffer);
|
memset(session, 0, sizeof(*session));
|
||||||
|
|
||||||
|
session->ev_base = base;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Early initialization of encoder because we need to parse
|
||||||
|
* config files to get the server configs, but while we are parsing
|
||||||
|
* config files, we need to buffer bind commands and all for the
|
||||||
|
* slave.
|
||||||
|
* Decoder is setup later.
|
||||||
|
*/
|
||||||
|
tmate_encoder_init(&session->encoder, NULL, &tmate_session);
|
||||||
|
|
||||||
|
session->min_sx = -1;
|
||||||
|
session->min_sy = -1;
|
||||||
|
|
||||||
|
TAILQ_INIT(&session->clients);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tmate_session_init(struct event_base *base)
|
void tmate_session_init(struct event_base *base)
|
||||||
{
|
{
|
||||||
tmate_session.ev_base = base;
|
__tmate_session_init(&tmate_session, base);
|
||||||
|
|
||||||
ssh_set_log_callback(ssh_log_function);
|
|
||||||
|
|
||||||
tmate_encoder_init(&tmate_session.encoder);
|
|
||||||
tmate_decoder_init(&tmate_session.decoder);
|
|
||||||
|
|
||||||
TAILQ_INIT(&tmate_session.clients);
|
|
||||||
|
|
||||||
tmate_session.need_passphrase = 0;
|
|
||||||
tmate_session.passphrase = NULL;
|
|
||||||
|
|
||||||
/* The header will be written as soon as the first client connects */
|
|
||||||
tmate_write_header();
|
tmate_write_header();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,43 +7,15 @@
|
|||||||
#include "tmate.h"
|
#include "tmate.h"
|
||||||
#include "window-copy.h"
|
#include "window-copy.h"
|
||||||
|
|
||||||
static void consume_channel(struct tmate_ssh_client *client);
|
static void on_ssh_client_event(struct tmate_ssh_client *client);
|
||||||
static void flush_input_stream(struct tmate_ssh_client *client);
|
static void __on_ssh_client_event(evutil_socket_t fd, short what, void *arg);
|
||||||
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 printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client,
|
||||||
static void printflike(2, 3) kill_session(struct tmate_ssh_client *client,
|
|
||||||
const char *fmt, ...);
|
const char *fmt, ...);
|
||||||
static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client,
|
static void printflike(2, 3) reconnect_ssh_client(struct tmate_ssh_client *client,
|
||||||
const char *fmt, ...);
|
const char *fmt, ...);
|
||||||
static void on_session_event(struct tmate_ssh_client *client);
|
|
||||||
|
|
||||||
static void register_session_fd_event(struct tmate_ssh_client *client)
|
static void read_channel(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, client->tmate_session->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)
|
|
||||||
{
|
|
||||||
struct tmate_encoder *encoder = &client->tmate_session->encoder;
|
|
||||||
|
|
||||||
if (!event_initialized(&encoder->ev_readable)) {
|
|
||||||
event_assign(&encoder->ev_readable, client->tmate_session->ev_base, -1,
|
|
||||||
EV_READ | EV_PERSIST, __flush_input_stream, client);
|
|
||||||
event_add(&encoder->ev_readable, NULL);
|
|
||||||
client->has_encoder = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void consume_channel(struct tmate_ssh_client *client)
|
|
||||||
{
|
{
|
||||||
struct tmate_decoder *decoder = &client->tmate_session->decoder;
|
struct tmate_decoder *decoder = &client->tmate_session->decoder;
|
||||||
char *buf;
|
char *buf;
|
||||||
@ -53,7 +25,7 @@ static void consume_channel(struct tmate_ssh_client *client)
|
|||||||
tmate_decoder_get_buffer(decoder, &buf, &len);
|
tmate_decoder_get_buffer(decoder, &buf, &len);
|
||||||
len = ssh_channel_read_nonblocking(client->channel, buf, len, 0);
|
len = ssh_channel_read_nonblocking(client->channel, buf, len, 0);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
reconnect_session(client, "Error reading from channel: %s",
|
reconnect_ssh_client(client, "Error reading from channel: %s",
|
||||||
ssh_get_error(client->session));
|
ssh_get_error(client->session));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -65,8 +37,43 @@ static void consume_channel(struct tmate_ssh_client *client)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void connection_complete(struct tmate_ssh_client *connected_client)
|
static void on_decoder_read(void *userdata, struct tmate_unpacker *uk)
|
||||||
{
|
{
|
||||||
|
struct tmate_ssh_client *client = userdata;
|
||||||
|
tmate_dispatch_slave_message(client->tmate_session, uk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_encoder_write(void *userdata, struct evbuffer *buffer)
|
||||||
|
{
|
||||||
|
struct tmate_ssh_client *client = userdata;
|
||||||
|
ssize_t len, written;
|
||||||
|
unsigned char *buf;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
len = evbuffer_get_length(buffer);
|
||||||
|
if (!len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
buf = evbuffer_pullup(buffer, -1);
|
||||||
|
|
||||||
|
written = ssh_channel_write(client->channel, buf, len);
|
||||||
|
if (written < 0) {
|
||||||
|
reconnect_ssh_client(client, "Error writing to channel: %s",
|
||||||
|
ssh_get_error(client->session));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
evbuffer_drain(buffer, written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_ssh_auth_server_complete(struct tmate_ssh_client *connected_client)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The first ssh connection succeeded. Hopefully this one offers the
|
||||||
|
* best latency. We can now kill the other ssh clients that are trying
|
||||||
|
* to connect.
|
||||||
|
*/
|
||||||
struct tmate_session *session = connected_client->tmate_session;
|
struct tmate_session *session = connected_client->tmate_session;
|
||||||
struct tmate_ssh_client *client, *tmp_client;
|
struct tmate_ssh_client *client, *tmp_client;
|
||||||
|
|
||||||
@ -75,7 +82,7 @@ static void connection_complete(struct tmate_ssh_client *connected_client)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
assert(!client->has_encoder);
|
assert(!client->has_encoder);
|
||||||
kill_session(client, NULL);
|
kill_ssh_client(client, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +122,7 @@ static void on_passphrase_read(const char *passphrase, void *private)
|
|||||||
struct tmate_ssh_client *client = private;
|
struct tmate_ssh_client *client = private;
|
||||||
|
|
||||||
client->tmate_session->passphrase = xstrdup(passphrase);
|
client->tmate_session->passphrase = xstrdup(passphrase);
|
||||||
on_session_event(client);
|
on_ssh_client_event(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void request_passphrase(struct tmate_ssh_client *client)
|
static void request_passphrase(struct tmate_ssh_client *client)
|
||||||
@ -155,7 +162,21 @@ static void request_passphrase(struct tmate_ssh_client *client)
|
|||||||
data->password_cb_private = client;
|
data->password_cb_private = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_session_event(struct tmate_ssh_client *client)
|
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, client->tmate_session->ev_base,
|
||||||
|
ssh_get_fd(client->session),
|
||||||
|
EV_READ | EV_PERSIST, __on_ssh_client_event, client);
|
||||||
|
event_add(&client->ev_ssh, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_ssh_client_event(struct tmate_ssh_client *client)
|
||||||
{
|
{
|
||||||
char *identity;
|
char *identity;
|
||||||
ssh_key pubkey;
|
ssh_key pubkey;
|
||||||
@ -214,7 +235,7 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
register_session_fd_event(client);
|
register_session_fd_event(client);
|
||||||
return;
|
return;
|
||||||
case SSH_ERROR:
|
case SSH_ERROR:
|
||||||
reconnect_session(client, "Error connecting: %s",
|
reconnect_ssh_client(client, "Error connecting: %s",
|
||||||
ssh_get_error(session));
|
ssh_get_error(session));
|
||||||
return;
|
return;
|
||||||
case SSH_OK:
|
case SSH_OK:
|
||||||
@ -229,7 +250,7 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
tmate_fatal("ssh_get_publickey");
|
tmate_fatal("ssh_get_publickey");
|
||||||
|
|
||||||
if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hash_len) < 0) {
|
if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hash_len) < 0) {
|
||||||
kill_session(client, "Cannot authenticate server");
|
kill_ssh_client(client, "Cannot authenticate server");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +280,7 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
free(hash_str);
|
free(hash_str);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
kill_session(client, "Cannot authenticate server");
|
kill_ssh_client(client, "Cannot authenticate server");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +291,7 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
* otherwise the speed test would be biased.
|
* otherwise the speed test would be biased.
|
||||||
*/
|
*/
|
||||||
tmate_debug("Connected to %s", client->server_ip);
|
tmate_debug("Connected to %s", client->server_ip);
|
||||||
connection_complete(client);
|
on_ssh_auth_server_complete(client);
|
||||||
client->state = SSH_AUTH_CLIENT;
|
client->state = SSH_AUTH_CLIENT;
|
||||||
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
@ -286,13 +307,15 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
if (client->tmate_session->need_passphrase)
|
if (client->tmate_session->need_passphrase)
|
||||||
request_passphrase(client);
|
request_passphrase(client);
|
||||||
else
|
else
|
||||||
kill_session(client, "SSH keys not found. Run 'ssh-keygen' to create keys and try again.");
|
kill_ssh_client(client, "SSH keys not found."
|
||||||
|
" Run 'ssh-keygen' to create keys and try again.");
|
||||||
|
|
||||||
if (client->tried_passphrase)
|
if (client->tried_passphrase)
|
||||||
tmate_status_message("Can't load SSH key. Try typing passphrase again in case of typo. ctrl-c to abort.");
|
tmate_status_message("Can't load SSH key."
|
||||||
|
" Try typing passphrase again in case of typo. ctrl-c to abort.");
|
||||||
return;
|
return;
|
||||||
case SSH_AUTH_ERROR:
|
case SSH_AUTH_ERROR:
|
||||||
reconnect_session(client, "Auth error: %s",
|
reconnect_ssh_client(client, "Auth error: %s",
|
||||||
ssh_get_error(session));
|
ssh_get_error(session));
|
||||||
return;
|
return;
|
||||||
case SSH_AUTH_SUCCESS:
|
case SSH_AUTH_SUCCESS:
|
||||||
@ -306,7 +329,7 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
case SSH_AGAIN:
|
case SSH_AGAIN:
|
||||||
return;
|
return;
|
||||||
case SSH_ERROR:
|
case SSH_ERROR:
|
||||||
reconnect_session(client, "Error opening channel: %s",
|
reconnect_ssh_client(client, "Error opening channel: %s",
|
||||||
ssh_get_error(session));
|
ssh_get_error(session));
|
||||||
return;
|
return;
|
||||||
case SSH_OK:
|
case SSH_OK:
|
||||||
@ -320,7 +343,7 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
case SSH_AGAIN:
|
case SSH_AGAIN:
|
||||||
return;
|
return;
|
||||||
case SSH_ERROR:
|
case SSH_ERROR:
|
||||||
reconnect_session(client, "Error initializing tmate: %s",
|
reconnect_ssh_client(client, "Error initializing tmate: %s",
|
||||||
ssh_get_error(session));
|
ssh_get_error(session));
|
||||||
return;
|
return;
|
||||||
case SSH_OK:
|
case SSH_OK:
|
||||||
@ -330,63 +353,31 @@ static void on_session_event(struct tmate_ssh_client *client)
|
|||||||
ssh_set_blocking(session, 1);
|
ssh_set_blocking(session, 1);
|
||||||
|
|
||||||
client->state = SSH_READY;
|
client->state = SSH_READY;
|
||||||
register_input_stream_event(client);
|
|
||||||
flush_input_stream(client);
|
tmate_encoder_set_ready_callback(&client->tmate_session->encoder,
|
||||||
|
on_encoder_write, client);
|
||||||
|
tmate_decoder_init(&client->tmate_session->decoder,
|
||||||
|
on_decoder_read, client);
|
||||||
/* fall through */
|
/* fall through */
|
||||||
}
|
}
|
||||||
|
|
||||||
case SSH_READY:
|
case SSH_READY:
|
||||||
consume_channel(client);
|
read_channel(client);
|
||||||
if (!ssh_is_connected(session)) {
|
if (!ssh_is_connected(session)) {
|
||||||
reconnect_session(client, "Disconnected");
|
reconnect_ssh_client(client, "Disconnected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flush_input_stream(struct tmate_ssh_client *client)
|
static void __on_ssh_client_event(__unused evutil_socket_t fd, __unused short what, void *arg)
|
||||||
{
|
{
|
||||||
struct tmate_encoder *encoder = &client->tmate_session->encoder;
|
on_ssh_client_event(arg);
|
||||||
struct evbuffer *evb = 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) {
|
|
||||||
reconnect_session(client, "Error writing to channel: %s",
|
|
||||||
ssh_get_error(client->session));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
evbuffer_drain(evb, written);
|
static void __kill_ssh_client(struct tmate_ssh_client *client,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __flush_input_stream(__unused evutil_socket_t fd, __unused short what, void *arg)
|
|
||||||
{
|
|
||||||
flush_input_stream(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __on_session_event(__unused evutil_socket_t fd, __unused short what, void *arg)
|
|
||||||
{
|
|
||||||
on_session_event(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __kill_session(struct tmate_ssh_client *client,
|
|
||||||
const char *fmt, va_list va)
|
const char *fmt, va_list va)
|
||||||
{
|
{
|
||||||
struct tmate_encoder *encoder;
|
|
||||||
|
|
||||||
if (fmt && TAILQ_EMPTY(&client->tmate_session->clients))
|
if (fmt && TAILQ_EMPTY(&client->tmate_session->clients))
|
||||||
__tmate_status_message(fmt, va);
|
__tmate_status_message(fmt, va);
|
||||||
else
|
else
|
||||||
@ -397,13 +388,6 @@ static void __kill_session(struct tmate_ssh_client *client,
|
|||||||
client->ev_ssh.ev_flags = 0;
|
client->ev_ssh.ev_flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->has_encoder) {
|
|
||||||
encoder = &client->tmate_session->encoder;
|
|
||||||
event_del(&encoder->ev_readable);
|
|
||||||
encoder->ev_readable.ev_flags = 0;
|
|
||||||
client->has_encoder = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->session) {
|
if (client->session) {
|
||||||
/* ssh_free() also frees the associated channels. */
|
/* ssh_free() also frees the associated channels. */
|
||||||
ssh_free(client->session);
|
ssh_free(client->session);
|
||||||
@ -414,7 +398,7 @@ static void __kill_session(struct tmate_ssh_client *client,
|
|||||||
client->state = SSH_NONE;
|
client->state = SSH_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printflike(2, 3) kill_session(struct tmate_ssh_client *client,
|
static void kill_ssh_client(struct tmate_ssh_client *client,
|
||||||
const char *fmt, ...)
|
const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@ -422,27 +406,27 @@ static void printflike(2, 3) kill_session(struct tmate_ssh_client *client,
|
|||||||
TAILQ_REMOVE(&client->tmate_session->clients, client, node);
|
TAILQ_REMOVE(&client->tmate_session->clients, client, node);
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
__kill_session(client, fmt, ap);
|
__kill_ssh_client(client, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
free(client->server_ip);
|
free(client->server_ip);
|
||||||
free(client);
|
free(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void connect_session(struct tmate_ssh_client *client)
|
static void connect_ssh_client(struct tmate_ssh_client *client)
|
||||||
{
|
{
|
||||||
if (!client->session) {
|
if (!client->session) {
|
||||||
client->state = SSH_INIT;
|
client->state = SSH_INIT;
|
||||||
on_session_event(client);
|
on_ssh_client_event(client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_reconnect_timer(__unused evutil_socket_t fd, __unused short what, void *arg)
|
static void on_reconnect_timer(__unused evutil_socket_t fd, __unused short what, void *arg)
|
||||||
{
|
{
|
||||||
connect_session(arg);
|
connect_ssh_client(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client,
|
static void reconnect_ssh_client(struct tmate_ssh_client *client,
|
||||||
const char *fmt, ...)
|
const char *fmt, ...)
|
||||||
{
|
{
|
||||||
/* struct timeval tv; */
|
/* struct timeval tv; */
|
||||||
@ -453,7 +437,7 @@ static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
__kill_session(client, fmt, ap);
|
__kill_ssh_client(client, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
/* Not yet implemented... */
|
/* Not yet implemented... */
|
||||||
@ -464,12 +448,20 @@ static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ssh_log_function(int priority, const char *function,
|
||||||
|
const char *buffer, __unused void *userdata)
|
||||||
|
{
|
||||||
|
tmate_debug("[%d] [%s] %s", priority, function, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session,
|
struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session,
|
||||||
const char *server_ip)
|
const char *server_ip)
|
||||||
{
|
{
|
||||||
struct tmate_ssh_client *client;
|
struct tmate_ssh_client *client;
|
||||||
client = xmalloc(sizeof(*client));
|
client = xmalloc(sizeof(*client));
|
||||||
|
|
||||||
|
ssh_set_log_callback(ssh_log_function);
|
||||||
|
|
||||||
memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks));
|
memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks));
|
||||||
ssh_callbacks_init(&client->ssh_callbacks);
|
ssh_callbacks_init(&client->ssh_callbacks);
|
||||||
client->ssh_callbacks.userdata = client;
|
client->ssh_callbacks.userdata = client;
|
||||||
@ -489,7 +481,7 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session,
|
|||||||
evtimer_assign(&client->ev_ssh_reconnect, session->ev_base,
|
evtimer_assign(&client->ev_ssh_reconnect, session->ev_base,
|
||||||
on_reconnect_timer, client);
|
on_reconnect_timer, client);
|
||||||
|
|
||||||
connect_session(client);
|
connect_ssh_client(client);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
103
tmate.h
103
tmate.h
@ -14,30 +14,66 @@
|
|||||||
#define tmate_info(...) log_debug("[tmate] I " __VA_ARGS__)
|
#define tmate_info(...) log_debug("[tmate] I " __VA_ARGS__)
|
||||||
#define tmate_fatal(...) fatalx("[tmate] " __VA_ARGS__)
|
#define tmate_fatal(...) fatalx("[tmate] " __VA_ARGS__)
|
||||||
|
|
||||||
/* tmate-encoder.c */
|
/* tmate-msgpack.c */
|
||||||
|
|
||||||
#define TMATE_MAX_MESSAGE_SIZE (16*1024)
|
typedef void tmate_encoder_write_cb(void *userdata, struct evbuffer *buffer);
|
||||||
|
|
||||||
#define TMATE_PROTOCOL_VERSION 5
|
|
||||||
|
|
||||||
enum tmate_commands {
|
|
||||||
TMATE_HEADER,
|
|
||||||
TMATE_SYNC_LAYOUT,
|
|
||||||
TMATE_PTY_DATA,
|
|
||||||
TMATE_EXEC_CMD,
|
|
||||||
TMATE_FAILED_CMD,
|
|
||||||
TMATE_STATUS,
|
|
||||||
TMATE_SYNC_COPY_MODE,
|
|
||||||
TMATE_WRITE_COPY_MODE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tmate_encoder {
|
struct tmate_encoder {
|
||||||
msgpack_packer pk;
|
msgpack_packer pk;
|
||||||
|
tmate_encoder_write_cb *ready_callback;
|
||||||
|
void *userdata;
|
||||||
struct evbuffer *buffer;
|
struct evbuffer *buffer;
|
||||||
struct event ev_readable;
|
struct event ev_buffer;
|
||||||
|
bool ev_active;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void tmate_encoder_init(struct tmate_encoder *encoder);
|
extern void tmate_encoder_init(struct tmate_encoder *encoder,
|
||||||
|
tmate_encoder_write_cb *callback,
|
||||||
|
void *userdata);
|
||||||
|
extern void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder,
|
||||||
|
tmate_encoder_write_cb *callback,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
extern void msgpack_pack_string(msgpack_packer *pk, const char *str);
|
||||||
|
extern void msgpack_pack_boolean(msgpack_packer *pk, bool value);
|
||||||
|
|
||||||
|
#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, __VA_ARGS__)
|
||||||
|
|
||||||
|
struct tmate_unpacker;
|
||||||
|
struct tmate_decoder;
|
||||||
|
typedef void tmate_decoder_reader(void *userdata, struct tmate_unpacker *uk);
|
||||||
|
|
||||||
|
struct tmate_decoder {
|
||||||
|
struct msgpack_unpacker unpacker;
|
||||||
|
tmate_decoder_reader *reader;
|
||||||
|
void *userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata);
|
||||||
|
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);
|
||||||
|
|
||||||
|
struct tmate_unpacker {
|
||||||
|
int argc;
|
||||||
|
msgpack_object *argv;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj);
|
||||||
|
extern void tmate_decoder_error(void);
|
||||||
|
extern int64_t unpack_int(struct tmate_unpacker *uk);
|
||||||
|
extern bool unpack_bool(struct tmate_unpacker *uk);
|
||||||
|
extern void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len);
|
||||||
|
extern char *unpack_string(struct tmate_unpacker *uk);
|
||||||
|
extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested);
|
||||||
|
|
||||||
|
#define unpack_each(nested_uk, tmp_uk, uk) \
|
||||||
|
for (unpack_array(uk, tmp_uk); \
|
||||||
|
(tmp_uk)->argc > 0 && (init_unpacker(nested_uk, (tmp_uk)->argv[0]), 1); \
|
||||||
|
(tmp_uk)->argv++, (tmp_uk)->argc--)
|
||||||
|
|
||||||
|
/* tmate-encoder.c */
|
||||||
|
|
||||||
|
#define TMATE_PROTOCOL_VERSION 5
|
||||||
|
|
||||||
extern void tmate_write_header(void);
|
extern void tmate_write_header(void);
|
||||||
extern void tmate_sync_layout(void);
|
extern void tmate_sync_layout(void);
|
||||||
@ -51,28 +87,9 @@ extern void tmate_write_copy_mode(struct window_pane *wp, const char *str);
|
|||||||
|
|
||||||
/* tmate-decoder.c */
|
/* tmate-decoder.c */
|
||||||
|
|
||||||
enum tmate_client_commands {
|
struct tmate_session;
|
||||||
TMATE_NOTIFY,
|
extern void tmate_dispatch_slave_message(struct tmate_session *session,
|
||||||
TMATE_CLIENT_PANE_KEY,
|
struct tmate_unpacker *uk);
|
||||||
TMATE_CLIENT_RESIZE,
|
|
||||||
TMATE_CLIENT_EXEC_CMD,
|
|
||||||
TMATE_CLIENT_ENV,
|
|
||||||
TMATE_CLIENT_READY,
|
|
||||||
TMATE_CLIENT_PANE_TMUX_KEY,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tmate_decoder {
|
|
||||||
struct msgpack_unpacker unpacker;
|
|
||||||
int ready;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 */
|
/* tmate-ssh-client.c */
|
||||||
|
|
||||||
@ -132,6 +149,12 @@ struct tmate_session {
|
|||||||
struct tmate_encoder encoder;
|
struct tmate_encoder encoder;
|
||||||
struct tmate_decoder decoder;
|
struct tmate_decoder decoder;
|
||||||
|
|
||||||
|
/* True when the slave has sent all the environment variables */
|
||||||
|
int tmate_env_ready;
|
||||||
|
|
||||||
|
int min_sx;
|
||||||
|
int min_sy;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This list contains one connection per IP. The first connected
|
* This list contains one connection per IP. The first connected
|
||||||
* client wins, and saved in *client. When we have a winner, the
|
* client wins, and saved in *client. When we have a winner, the
|
||||||
@ -147,7 +170,7 @@ extern void tmate_session_init(struct event_base *base);
|
|||||||
extern void tmate_session_start(void);
|
extern void tmate_session_start(void);
|
||||||
|
|
||||||
/* tmate-debug.c */
|
/* tmate-debug.c */
|
||||||
extern void tmate_print_trace(void);
|
extern void tmate_print_stack_trace(void);
|
||||||
extern void tmate_catch_sigsegv(void);
|
extern void tmate_catch_sigsegv(void);
|
||||||
|
|
||||||
/* tmate-msg.c */
|
/* tmate-msg.c */
|
||||||
|
Loading…
Reference in New Issue
Block a user