mirror of
https://github.com/tmate-io/tmate.git
synced 2024-11-08 09:14:22 +01:00
281 lines
5.6 KiB
C
281 lines
5.6 KiB
C
#include "tmate.h"
|
|
|
|
int tmate_sx = -1;
|
|
int tmate_sy = -1;
|
|
|
|
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);
|
|
tmate_status_message("%s", msg);
|
|
free(msg);
|
|
}
|
|
|
|
static void tmate_client_pane_key(struct tmate_unpacker *uk)
|
|
{
|
|
struct session *s;
|
|
struct window *w;
|
|
struct window_pane *wp;
|
|
|
|
int key = unpack_int(uk);
|
|
|
|
s = RB_MIN(sessions, &sessions);
|
|
if (!s)
|
|
return;
|
|
|
|
w = s->curw->window;
|
|
if (!w)
|
|
return;
|
|
|
|
wp = w->active;
|
|
if (!wp)
|
|
return;
|
|
|
|
window_pane_key(wp, NULL, s, key, NULL);
|
|
}
|
|
|
|
static struct window_pane *find_window_pane(struct session *s, int pane_id)
|
|
{
|
|
struct window *w;
|
|
struct window_pane *wp;
|
|
struct winlink *wl;
|
|
|
|
w = s->curw->window;
|
|
if (!w)
|
|
goto slow_path;
|
|
|
|
wp = w->active;
|
|
if (!wp)
|
|
goto slow_path;
|
|
if (pane_id == -1 || (int)wp->id == pane_id)
|
|
return wp;
|
|
|
|
slow_path:
|
|
if (pane_id == -1)
|
|
return NULL;
|
|
|
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
|
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
|
|
if ((int)wp->id == pane_id)
|
|
return wp;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void tmate_client_pane_tmux_key(struct tmate_unpacker *uk)
|
|
{
|
|
struct session *s;
|
|
struct window_pane *wp;
|
|
|
|
int pane_id = unpack_int(uk);
|
|
key_code key = unpack_int(uk);
|
|
|
|
s = RB_MIN(sessions, &sessions);
|
|
if (!s)
|
|
return;
|
|
|
|
wp = find_window_pane(s, pane_id);
|
|
if (!wp)
|
|
return;
|
|
|
|
window_pane_key(wp, NULL, s, key, NULL);
|
|
}
|
|
|
|
static void tmate_client_resize(struct tmate_unpacker *uk)
|
|
{
|
|
/* TODO This is sad, we might want our own client. */
|
|
tmate_sx = unpack_int(uk);
|
|
tmate_sy = unpack_int(uk);
|
|
recalculate_sizes();
|
|
|
|
/* TODO Handle reconnection cases */
|
|
}
|
|
|
|
extern char **cfg_causes;
|
|
extern u_int cfg_ncauses;
|
|
|
|
static void tmate_client_exec_cmd(struct tmate_unpacker *uk)
|
|
{
|
|
struct cmd_q *cmd_q;
|
|
struct cmd_list *cmdlist;
|
|
char *cause;
|
|
u_int i;
|
|
|
|
int client_id = unpack_int(uk);
|
|
char *cmd_str = unpack_string(uk);
|
|
|
|
if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) {
|
|
tmate_failed_cmd(client_id, cause);
|
|
free(cause);
|
|
goto out;
|
|
}
|
|
|
|
cmd_q = cmdq_new(NULL);
|
|
cmdq_run(cmd_q, cmdlist, NULL);
|
|
cmd_list_free(cmdlist);
|
|
cmdq_free(cmd_q);
|
|
|
|
/* error messages land in cfg_causes */
|
|
for (i = 0; i < cfg_ncauses; i++) {
|
|
tmate_failed_cmd(client_id, cfg_causes[i]);
|
|
free(cfg_causes[i]);
|
|
}
|
|
|
|
free(cfg_causes);
|
|
cfg_causes = NULL;
|
|
cfg_ncauses = 0;
|
|
|
|
out:
|
|
free(cmd_str);
|
|
}
|
|
|
|
static void tmate_client_env(struct tmate_unpacker *uk)
|
|
{
|
|
char *name = unpack_string(uk);
|
|
char *value = unpack_string(uk);
|
|
|
|
tmate_set_env(name, value);
|
|
|
|
free(name);
|
|
free(value);
|
|
}
|
|
|
|
|
|
extern void signal_waiting_clients(const char *name);
|
|
static void tmate_client_ready(struct tmate_decoder *decoder,
|
|
__unused struct tmate_unpacker *uk)
|
|
{
|
|
decoder->ready = 1;
|
|
signal_waiting_clients("tmate-ready");
|
|
}
|
|
|
|
static void handle_message(struct tmate_decoder *decoder, msgpack_object obj)
|
|
{
|
|
struct tmate_unpacker _uk;
|
|
struct tmate_unpacker *uk = &_uk;
|
|
|
|
init_unpacker(uk, obj);
|
|
|
|
switch (unpack_int(uk)) {
|
|
case TMATE_NOTIFY: tmate_notify(uk); break;
|
|
case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break;
|
|
case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break;
|
|
case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break;
|
|
case TMATE_CLIENT_ENV: tmate_client_env(uk); break;
|
|
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;
|
|
}
|