1
0
mirror of https://github.com/tmate-io/tmate-ssh-server.git synced 2020-11-18 19:53:51 -08:00

Slave implementation draft

This commit is contained in:
Nicolas Viennot 2013-06-05 17:54:07 -04:00
parent 1b083aa0fd
commit dbc4ecc92d
40 changed files with 1369 additions and 99 deletions

4
.gitignore vendored
View File

@ -15,3 +15,7 @@ tmux
Makefile
Makefile.in
configure
keys/
tmate-server
cscope.*
tags

View File

@ -1,8 +1,8 @@
# $Id$
# Obvious program stuff.
bin_PROGRAMS = tmux
dist_man1_MANS = tmux.1
bin_PROGRAMS = tmate-server
dist_man1_MANS = tmate.1
# Distribution tarball options.
EXTRA_DIST = \
@ -21,6 +21,9 @@ if IS_GLIBC
CFLAGS += -D_GNU_SOURCE
endif
CFLAGS += -Wno-unused-parameter -Wno-unused-variable
# Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly
# different flags.
if IS_GCC
@ -57,7 +60,7 @@ CFLAGS += -erroff=E_EMPTY_DECLARATION
endif
# List of sources.
dist_tmux_SOURCES = \
dist_tmate_server_SOURCES = \
arguments.c \
attributes.c \
cfg.c \
@ -171,7 +174,13 @@ dist_tmux_SOURCES = \
session.c \
signal.c \
status.c \
tmux.c \
tmate-debug.c \
tmate-decoder.c \
tmate-encoder.c \
tmate-server.c \
tmate-ssh-client-pty.c \
tmate-ssh-client.c \
tmate-ssh-server.c \
tty-acs.c \
tty-keys.c \
tty-term.c \
@ -183,66 +192,51 @@ dist_tmux_SOURCES = \
window.c \
xmalloc.c \
xterm-keys.c
nodist_tmux_SOURCES = osdep-@PLATFORM@.c
nodist_tmate_server_SOURCES = osdep-@PLATFORM@.c
# Pile in all the compat/ stuff that is needed.
if NO_FORKPTY
nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c
nodist_tmate_server_SOURCES += compat/forkpty-@PLATFORM@.c
endif
if NO_IMSG
nodist_tmux_SOURCES += compat/imsg.c compat/imsg-buffer.c
nodist_tmate_server_SOURCES += compat/imsg.c compat/imsg-buffer.c
endif
if NO_CLOSEFROM
nodist_tmux_SOURCES += compat/closefrom.c
nodist_tmate_server_SOURCES += compat/closefrom.c
endif
if NO_DAEMON
nodist_tmux_SOURCES += compat/daemon.c
nodist_tmate_server_SOURCES += compat/daemon.c
endif
if NO_SETENV
nodist_tmux_SOURCES += compat/setenv.c
nodist_tmate_server_SOURCES += compat/setenv.c
endif
if NO_STRLCAT
nodist_tmux_SOURCES += compat/strlcat.c
nodist_tmate_server_SOURCES += compat/strlcat.c
endif
if NO_STRLCPY
nodist_tmux_SOURCES += compat/strlcpy.c
nodist_tmate_server_SOURCES += compat/strlcpy.c
endif
if NO_ASPRINTF
nodist_tmux_SOURCES += compat/asprintf.c
nodist_tmate_server_SOURCES += compat/asprintf.c
endif
if NO_FGETLN
nodist_tmux_SOURCES += compat/fgetln.c
nodist_tmate_server_SOURCES += compat/fgetln.c
endif
if NO_GETOPT
nodist_tmux_SOURCES += compat/getopt.c
nodist_tmate_server_SOURCES += compat/getopt.c
endif
if NO_STRCASESTR
nodist_tmux_SOURCES += compat/strcasestr.c
nodist_tmate_server_SOURCES += compat/strcasestr.c
endif
if NO_STRSEP
nodist_tmux_SOURCES += compat/strsep.c
nodist_tmate_server_SOURCES += compat/strsep.c
endif
if NO_VIS
nodist_tmux_SOURCES += compat/vis.c compat/unvis.c
nodist_tmate_server_SOURCES += compat/vis.c compat/unvis.c
endif
if NO_STRTONUM
nodist_tmux_SOURCES += compat/strtonum.c
nodist_tmate_server_SOURCES += compat/strtonum.c
endif
if NO_B64_NTOP
nodist_tmux_SOURCES += compat/b64_ntop.c
nodist_tmate_server_SOURCES += compat/b64_ntop.c
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

View File

@ -205,6 +205,10 @@ client_main(int argc, char **argv, int flags)
cmd_list_free(cmdlist);
}
#ifdef TMATE_SLAVE
cmdflags &= ~CMD_STARTSERVER;
#endif
/*
* Check if this could be a nested session, if the command can't nest:
* if the socket path matches $TMUX, this is probably the same server.
@ -228,7 +232,6 @@ client_main(int argc, char **argv, int flags)
#ifdef HAVE_SETPROCTITLE
setproctitle("client (%s)", socket_path);
#endif
logfile("client");
/* Create imsg. */
imsg_init(&client_ibuf, fd);
@ -505,7 +508,7 @@ client_dispatch_wait(void *data)
return (0);
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
log_debug("got %d from server", imsg.hdr.type);
log_debug2("got %d from server", imsg.hdr.type);
switch (imsg.hdr.type) {
case MSG_EXIT:
case MSG_SHUTDOWN:
@ -544,7 +547,7 @@ client_dispatch_wait(void *data)
fatalx("bad MSG_STDERR");
memcpy(&stderrdata, imsg.data, sizeof stderrdata);
client_write(STDERR_FILENO, stderrdata.data, stderrdata.size);
client_write(fileno(stderr), stderrdata.data, stderrdata.size);
break;
case MSG_VERSION:
if (datalen != 0)
@ -565,7 +568,7 @@ client_dispatch_wait(void *data)
clear_signals(0);
shell_exec(shelldata.shell, shellcmd);
exit(1);
/* NOTREACHED */
case MSG_DETACH:
client_write_server(MSG_EXITING, NULL, 0);
@ -597,7 +600,7 @@ client_dispatch_attached(void)
return (0);
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
log_debug("got %d from server", imsg.hdr.type);
log_debug2("got %d from server", imsg.hdr.type);
switch (imsg.hdr.type) {
case MSG_DETACHKILL:
case MSG_DETACH:

View File

@ -45,6 +45,9 @@ const struct cmd_entry cmd_paste_buffer_entry = {
enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
{
#ifdef TMATE_SLAVE
return (CMD_RETURN_ERROR);
#else
struct args *args = self->args;
struct window_pane *wp;
struct session *s;
@ -99,4 +102,5 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
}
return (CMD_RETURN_NORMAL);
#endif
}

View File

@ -48,6 +48,9 @@ const struct cmd_entry cmd_pipe_pane_entry = {
enum cmd_retval
cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq)
{
#ifdef TMATE_SLAVE
return (CMD_RETURN_ERROR);
#else
struct args *args = self->args;
struct client *c;
struct window_pane *wp;
@ -128,6 +131,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq)
setblocking(wp->pipe_fd, 0);
return (CMD_RETURN_NORMAL);
}
#endif
}
void

View File

@ -43,6 +43,9 @@ const struct cmd_entry cmd_respawn_pane_entry = {
enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
{
#ifdef TMATE_SLAVE
return (CMD_RETURN_ERROR);
#else
struct args *args = self->args;
struct winlink *wl;
struct window *w;
@ -89,4 +92,5 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
environ_free(&env);
return (CMD_RETURN_NORMAL);
#endif
}

View File

@ -42,6 +42,9 @@ const struct cmd_entry cmd_respawn_window_entry = {
enum cmd_retval
cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq)
{
#ifdef TMATE_SLAVE
return (CMD_RETURN_ERROR);
#else
struct args *args = self->args;
struct winlink *wl;
struct window *w;
@ -98,4 +101,5 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq)
environ_free(&env);
return (CMD_RETURN_NORMAL);
#endif
}

View File

@ -52,6 +52,9 @@ const struct cmd_entry cmd_send_prefix_entry = {
enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq)
{
#ifdef TMATE_SLAVE
return (CMD_RETURN_ERROR);
#else
struct args *args = self->args;
struct window_pane *wp;
struct session *s;
@ -100,4 +103,5 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq)
}
return (CMD_RETURN_NORMAL);
#endif
}

View File

@ -123,8 +123,8 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq)
sizeof *gl->celldata;
}
cmdq_print(cmdq,
"%6u: %s %lu %d %u/%u, %zu bytes", j,
wp->tty, (u_long) wp->pid, wp->fd, lines,
"%6u: %s %lu %u/%u, %zu bytes", j,
wp->tty, (u_long) wp->pid, lines,
gd->hsize + gd->sy, size);
j++;
}

View File

@ -161,6 +161,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
}
/* Start or stop timers when automatic-rename changed. */
#ifndef TMATE_SLAVE
if (strcmp (oe->name, "automatic-rename") == 0) {
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
if ((w = ARRAY_ITEM(&windows, i)) == NULL)
@ -171,6 +172,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
evtimer_del(&w->name_timer);
}
}
#endif
/* Update sizes and redraw. May not need it but meh. */
recalculate_sizes();

View File

@ -52,6 +52,9 @@ cmd_split_window_key_binding(struct cmd *self, int key)
enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
{
#ifdef TMATE_SLAVE
return (CMD_RETURN_ERROR);
#else
struct args *args = self->args;
struct session *s;
struct winlink *wl;
@ -113,8 +116,6 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
}
hlimit = options_get_number(&s->options, "history-limit");
shell = options_get_string(&s->options, "default-shell");
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
if ((lc = layout_split_pane(wp, type, size, 0)) == NULL) {
@ -165,4 +166,5 @@ error:
cmdq_error(cmdq, "create pane failed: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
#endif
}

4
cmd.c
View File

@ -1290,6 +1290,9 @@ cmd_template_replace(const char *template, const char *s, int idx)
const char *
cmd_get_default_path(struct cmd_q *cmdq, const char *cwd)
{
#ifdef TMATE_SLAVE
return NULL;
#else
struct client *c = cmdq->client;
struct session *s;
struct environ_entry *envent;
@ -1361,4 +1364,5 @@ complete_path:
if (n > 0 && (size_t)n < sizeof path)
return (path);
return (s->cwd);
#endif
}

View File

@ -107,6 +107,9 @@ AC_MSG_RESULT($found_glibc)
# Look for clock_gettime. Must come before event_init.
AC_SEARCH_LIBS(clock_gettime, rt)
AC_SEARCH_LIBS(ssh_new, ssh)
AC_SEARCH_LIBS(msgpack_object_print, msgpack)
# Look for libevent.
PKG_CHECK_MODULES(
LIBEVENT,

13
create_keys.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
gen_key() {
keytype=$1
ks="${keytype}_"
key="keys/ssh_host_${ks}key"
if [ ! -e "${key}" ] ; then
ssh-keygen -t ${keytype} -f "${key}" -N ''
return $?
fi
}
mkdir -p keys
gen_key dsa && gen_key rsa && gen_key ecdsa || exit 1

View File

@ -421,7 +421,9 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_title", "%s", wp->base.title);
format_add(ft, "pane_id", "%%%u", wp->id);
format_add(ft, "pane_active", "%d", wp == wp->window->active);
#ifndef TMATE_SLAVE
format_add(ft, "pane_dead", "%d", wp->fd == -1);
#endif
format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
@ -432,12 +434,14 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_start_command", "%s", wp->cmd);
if (wp->cwd != NULL)
format_add(ft, "pane_start_path", "%s", wp->cwd);
#ifndef TMATE_SLAVE
if ((cwd = osdep_get_cwd(wp->fd)) != NULL)
format_add(ft, "pane_current_path", "%s", cwd);
if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) {
format_add(ft, "pane_current_command", "%s", cmd);
free(cmd);
}
#endif
format_add(ft, "cursor_x", "%d", wp->base.cx);
format_add(ft, "cursor_y", "%d", wp->base.cy);

View File

@ -23,6 +23,8 @@
#include "tmux.h"
#ifndef TMATE_SLAVE
/*
* This file is rather misleadingly named, it contains the code which takes a
* key code and translates it into something suitable to be sent to the
@ -253,3 +255,5 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m)
}
}
}
#endif

62
input.c
View File

@ -732,7 +732,11 @@ input_parse(struct window_pane *wp)
{
struct input_ctx *ictx = &wp->ictx;
const struct input_transition *itr;
#ifdef TMATE_SLAVE
struct evbuffer *evb = wp->event_input;
#else
struct evbuffer *evb = wp->event->input;
#endif
u_char *buf;
size_t len, off;
@ -760,7 +764,7 @@ input_parse(struct window_pane *wp)
/* Parse the input. */
while (off < len) {
ictx->ch = buf[off++];
log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name);
log_debug2("%s: '%c' %s", __func__, ictx->ch, ictx->state->name);
/* Find the transition. */
itr = ictx->state->transitions;
@ -848,6 +852,7 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
void
input_reply(struct input_ctx *ictx, const char *fmt, ...)
{
#ifndef TMATE_SLAVE
va_list ap;
char *reply;
@ -857,6 +862,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
bufferevent_write(ictx->wp->event, reply, strlen(reply));
free(reply);
#endif
}
/* Clear saved state. */
@ -936,7 +942,7 @@ input_c0_dispatch(struct input_ctx *ictx)
struct screen *s = sctx->s;
u_int trigger;
log_debug("%s: '%c", __func__, ictx->ch);
log_debug2("%s: '%c", __func__, ictx->ch);
switch (ictx->ch) {
case '\000': /* NUL */
@ -974,7 +980,7 @@ input_c0_dispatch(struct input_ctx *ictx)
ictx->cell.attr &= ~GRID_ATTR_CHARSET;
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
@ -1000,12 +1006,12 @@ input_esc_dispatch(struct input_ctx *ictx)
if (ictx->flags & INPUT_DISCARD)
return (0);
log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf);
log_debug2("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf);
entry = bsearch(ictx, input_esc_table, nitems(input_esc_table),
sizeof input_esc_table[0], input_table_compare);
if (entry == NULL) {
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
return (0);
}
@ -1080,13 +1086,13 @@ input_csi_dispatch(struct input_ctx *ictx)
return (0);
if (input_split(ictx) != 0)
return (0);
log_debug("%s: '%c' \"%s\" \"%s\"",
log_debug2("%s: '%c' \"%s\" \"%s\"",
__func__, ictx->ch, ictx->interm_buf, ictx->param_buf);
entry = bsearch(ictx, input_csi_table, nitems(input_csi_table),
sizeof input_csi_table[0], input_table_compare);
if (entry == NULL) {
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
return (0);
}
@ -1131,7 +1137,7 @@ input_csi_dispatch(struct input_ctx *ictx)
input_reply(ictx, "\033[?1;2c");
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1141,7 +1147,7 @@ input_csi_dispatch(struct input_ctx *ictx)
input_reply(ictx, "\033[>0;95;0c");
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1168,7 +1174,7 @@ input_csi_dispatch(struct input_ctx *ictx)
input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1195,7 +1201,7 @@ input_csi_dispatch(struct input_ctx *ictx)
}
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1211,7 +1217,7 @@ input_csi_dispatch(struct input_ctx *ictx)
screen_write_clearline(sctx);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1235,7 +1241,7 @@ input_csi_dispatch(struct input_ctx *ictx)
screen_write_mode_clear(&ictx->ctx, MODE_INSERT);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1280,7 +1286,7 @@ input_csi_dispatch(struct input_ctx *ictx)
screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1298,7 +1304,7 @@ input_csi_dispatch(struct input_ctx *ictx)
screen_write_mode_set(&ictx->ctx, MODE_INSERT);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1352,7 +1358,7 @@ input_csi_dispatch(struct input_ctx *ictx)
screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1366,7 +1372,7 @@ input_csi_dispatch(struct input_ctx *ictx)
bit_nclear(s->tabs, 0, screen_size_x(s) - 1);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
log_debug2("%s: unknown '%c'", __func__, ictx->ch);
break;
}
break;
@ -1539,7 +1545,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
if (ictx->flags & INPUT_DISCARD)
return (0);
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
log_debug2("%s: \"%s\"", __func__, ictx->input_buf);
/* Check for tmux prefix. */
if (ictx->input_len >= prefix_len &&
@ -1555,7 +1561,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
void
input_enter_osc(struct input_ctx *ictx)
{
log_debug("%s", __func__);
log_debug2("%s", __func__);
input_clear(ictx);
}
@ -1572,7 +1578,7 @@ input_exit_osc(struct input_ctx *ictx)
if (ictx->input_len < 1 || *p < '0' || *p > '9')
return;
log_debug("%s: \"%s\"", __func__, p);
log_debug2("%s: \"%s\"", __func__, p);
option = 0;
while (*p >= '0' && *p <= '9')
@ -1595,7 +1601,7 @@ input_exit_osc(struct input_ctx *ictx)
screen_set_cursor_colour(ictx->ctx.s, "");
break;
default:
log_debug("%s: unknown '%u'", __func__, option);
log_debug2("%s: unknown '%u'", __func__, option);
break;
}
}
@ -1604,7 +1610,7 @@ input_exit_osc(struct input_ctx *ictx)
void
input_enter_apc(struct input_ctx *ictx)
{
log_debug("%s", __func__);
log_debug2("%s", __func__);
input_clear(ictx);
}
@ -1615,7 +1621,7 @@ input_exit_apc(struct input_ctx *ictx)
{
if (ictx->flags & INPUT_DISCARD)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
log_debug2("%s: \"%s\"", __func__, ictx->input_buf);
screen_set_title(ictx->ctx.s, ictx->input_buf);
server_status_window(ictx->wp->window);
@ -1625,7 +1631,7 @@ input_exit_apc(struct input_ctx *ictx)
void
input_enter_rename(struct input_ctx *ictx)
{
log_debug("%s", __func__);
log_debug2("%s", __func__);
input_clear(ictx);
}
@ -1638,7 +1644,7 @@ input_exit_rename(struct input_ctx *ictx)
return;
if (!options_get_number(&ictx->wp->window->options, "allow-rename"))
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
log_debug2("%s: \"%s\"", __func__, ictx->input_buf);
window_set_name(ictx->wp->window, ictx->input_buf);
options_set_number(&ictx->wp->window->options, "automatic-rename", 0);
@ -1655,7 +1661,7 @@ input_utf8_open(struct input_ctx *ictx)
input_print(ictx);
return (-1);
}
log_debug("%s", __func__);
log_debug2("%s", __func__);
utf8_open(&ictx->utf8data, ictx->ch);
return (0);
@ -1665,7 +1671,7 @@ input_utf8_open(struct input_ctx *ictx)
int
input_utf8_add(struct input_ctx *ictx)
{
log_debug("%s", __func__);
log_debug2("%s", __func__);
utf8_append(&ictx->utf8data, ictx->ch);
return (0);
@ -1675,7 +1681,7 @@ input_utf8_add(struct input_ctx *ictx)
int
input_utf8_close(struct input_ctx *ictx)
{
log_debug("%s", __func__);
log_debug2("%s", __func__);
utf8_append(&ictx->utf8data, ictx->ch);

10
log.c
View File

@ -24,6 +24,7 @@
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "tmux.h"
@ -48,9 +49,14 @@ log_event_cb(unused int severity, const char *msg)
void
log_open(int level, const char *path)
{
log_file = fopen(path, "w");
if (path) {
log_file = fopen(path, "a");
if (log_file == NULL)
return;
} else {
log_file = stderr;
}
log_level = level;
setlinebuf(log_file);
@ -78,7 +84,7 @@ log_vwrite(const char *msg, va_list ap)
if (log_file == NULL)
return;
if (asprintf(&fmt, "%s\n", msg) == -1)
if (asprintf(&fmt, "[%d] %s\n", getpid(), msg) == -1)
exit(1);
if (vfprintf(log_file, fmt, ap) == -1)
exit(1);

18
names.c
View File

@ -26,6 +26,22 @@
#include "tmux.h"
#ifdef TMATE_SLAVE
char *
default_window_name(struct window *w)
{
return xstrdup("[tmux]");
}
void
queue_window_name(struct window *w)
{
}
#else
void window_name_callback(unused int, unused short, void *);
char *parse_window_name(const char *);
@ -128,3 +144,5 @@ parse_window_name(const char *in)
free(copy);
return (name);
}
#endif

View File

@ -167,6 +167,7 @@ paste_print(struct paste_buffer *pb, size_t width)
return (buf);
}
#ifndef TMATE_SLAVE
/* Paste into a window pane, filtering '\n' according to separator. */
void
paste_send_pane (struct paste_buffer *pb, struct window_pane *wp,
@ -192,3 +193,4 @@ paste_send_pane (struct paste_buffer *pb, struct window_pane *wp,
if (bracket)
bufferevent_write(wp->event, "\033[201~", 6);
}
#endif

View File

@ -21,6 +21,7 @@
#include <string.h>
#include "tmux.h"
#include "tmate.h"
/*
* Recalculate window and session sizes.
@ -71,6 +72,12 @@ recalculate_sizes(void)
ssy = c->tty.sy;
}
}
#ifdef TMATE_SLAVE
tmate_client_resize(ssx, ssy);
return;
#endif
if (ssx == UINT_MAX || ssy == UINT_MAX) {
s->flags |= SESSION_UNATTACHED;
continue;

View File

@ -21,6 +21,7 @@
#include <string.h>
#include "tmux.h"
#include "tmate.h"
int screen_redraw_cell_border1(struct window_pane *, u_int, u_int);
int screen_redraw_cell_border(struct client *, u_int, u_int);

View File

@ -27,6 +27,7 @@
#include <unistd.h>
#include "tmux.h"
#include "tmate.h"
void server_client_check_focus(struct window_pane *);
void server_client_check_resize(struct window_pane *);
@ -281,6 +282,7 @@ server_client_status_timer(void)
}
}
#ifndef TMATE_SLAVE
/* Check for mouse keys. */
void
server_client_check_mouse(struct client *c, struct window_pane *wp)
@ -333,6 +335,7 @@ server_client_check_mouse(struct client *c, struct window_pane *wp)
/* Update last and pass through to client. */
window_pane_mouse(wp, c->session, m);
}
#endif
/* Is this fast enough to probably be a paste? */
int
@ -354,6 +357,9 @@ server_client_assume_paste(struct session *s)
void
server_client_handle_key(struct client *c, int key)
{
#ifdef TMATE_SLAVE
tmate_client_key(key);
#else
struct session *s;
struct window *w;
struct window_pane *wp;
@ -479,6 +485,7 @@ server_client_handle_key(struct client *c, int key)
/* Dispatch the command. */
key_bindings_dispatch(bd, c);
#endif
}
/* Client functions that need to happen every loop. */
@ -513,13 +520,16 @@ server_client_loop(void)
w->flags &= ~WINDOW_REDRAW;
TAILQ_FOREACH(wp, &w->panes, entry) {
#ifndef TMATE_SLAVE
server_client_check_focus(wp);
server_client_check_resize(wp);
#endif
wp->flags &= ~PANE_REDRAW;
}
}
}
#ifndef TMATE_SLAVE
/* Check if pane should be resized. */
void
server_client_check_resize(struct window_pane *wp)
@ -597,6 +607,7 @@ focused:
bufferevent_write(wp->event, "\033[I", 3);
wp->flags |= PANE_FOCUSED;
}
#endif
/*
* Update cursor position and mode settings. The scroll region and attributes
@ -811,7 +822,7 @@ server_client_msg_dispatch(struct client *c)
continue;
}
log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
log_debug2("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
switch (imsg.hdr.type) {
case MSG_COMMAND:
if (datalen != sizeof commanddata)
@ -996,9 +1007,6 @@ server_client_msg_shell(struct client *c)
struct msg_shell_data data;
const char *shell;
shell = options_get_string(&global_s_options, "default-shell");
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);

View File

@ -64,7 +64,7 @@ server_write_client(
if (c->flags & CLIENT_BAD)
return (-1);
log_debug("writing %d to client %d", type, c->ibuf.fd);
log_debug2("writing %d to client %d", type, c->ibuf.fd);
error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1,
(void *) buf, len);
if (error == 1)
@ -351,6 +351,7 @@ void
server_destroy_pane(struct window_pane *wp)
{
struct window *w = wp->window;
#ifndef TMATE_SLAVE
int old_fd;
struct screen_write_ctx ctx;
struct grid_cell gc;
@ -376,6 +377,7 @@ server_destroy_pane(struct window_pane *wp)
wp->flags |= PANE_REDRAW;
return;
}
#endif
server_unzoom_window(w);
layout_close_pane(wp);

View File

@ -36,6 +36,7 @@
#include <unistd.h>
#include "tmux.h"
#include "tmate.h"
/*
* Main server functions.
@ -105,10 +106,13 @@ server_create_socket(void)
int
server_start(int lockfd, char *lockfile)
{
#ifndef TMATE_SLAVE
int pair[2];
#endif
struct timeval tv;
char *cause;
#ifndef TMATE_SLAVE
/* The first client is special and gets a socketpair; create it. */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
fatal("socketpair failed");
@ -135,8 +139,9 @@ server_start(int lockfd, char *lockfile)
if (event_reinit(ev_base) != 0)
fatal("event_reinit failed");
clear_signals(0);
logfile("server");
#endif
log_debug("server started, pid %ld", (long) getpid());
ARRAY_INIT(&windows);
@ -158,11 +163,14 @@ server_start(int lockfd, char *lockfile)
#endif
server_fd = server_create_socket();
#ifndef TMATE_SLAVE
server_client_create(pair[1]);
unlink(lockfile);
free(lockfile);
close(lockfd);
#endif
cfg_cmd_q = cmdq_new(NULL);
cfg_cmd_q->emptyfn = cfg_default_done;
@ -203,7 +211,8 @@ server_start(int lockfd, char *lockfile)
void
server_loop(void)
{
while (!server_should_shutdown()) {
while (!server_shutdown) {
//while (!server_should_shutdown()) {
event_loop(EVLOOP_ONCE);
server_window_loop();

View File

@ -25,6 +25,7 @@
#include <time.h>
#include "tmux.h"
#include "tmate.h"
/* Global session list. */
struct sessions sessions;
@ -244,8 +245,6 @@ session_new(struct session *s,
environ_copy(&s->environ, &env);
server_fill_environ(s, &env);
shell = options_get_string(&s->options, "default-shell");
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
hlimit = options_get_number(&s->options, "history-limit");

76
tmate-debug.c Normal file
View File

@ -0,0 +1,76 @@
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include "tmate.h"
#if DEBUG
static int print_resolved_stack_frame(const char *frame)
{
char file[100];
char cmd[200];
char output[300];
char address[20];
char *line;
FILE *ps;
static regex_t _regex;
static regex_t *regex;
regmatch_t matches[3];
if (!regex) {
if (regcomp(&_regex, "(.+)\\(\\) \\[([^]]+)\\]", REG_EXTENDED))
return -1;
regex = &_regex;
}
if (regexec(regex, frame, 3, matches, 0))
return -1;
memcpy(file, &frame[matches[1].rm_so], matches[1].rm_eo - matches[1].rm_so);
file[matches[1].rm_eo - matches[1].rm_so] = 0;
memcpy(address, &frame[matches[2].rm_so], matches[2].rm_eo - matches[2].rm_so);
address[matches[2].rm_eo - matches[2].rm_so] = 0;
sprintf(cmd, "addr2line -e %s %s -f -p -s", file, address);
ps = popen(cmd, "r");
if (!ps)
return -1;
line = fgets(output, sizeof(output), ps);
pclose(ps);
if (!line)
return -1;
line[strlen(line)-1] = 0; /* remove \n */
tmate_debug("%s(%s) [%s]", file, line, address);
return 0;
}
#endif
void
tmate_print_trace (void)
{
void *array[20];
size_t size;
char **strings;
size_t i;
size = backtrace (array, 20);
strings = backtrace_symbols (array, size);
tmate_debug ("============ %zd stack frames ============", size);
for (i = 1; i < size; i++) {
#if DEBUG
if (print_resolved_stack_frame(strings[i]) < 0)
#endif
tmate_debug("%s", strings[i]);
}
free (strings);
}

260
tmate-decoder.c Normal file
View File

@ -0,0 +1,260 @@
#include "tmate.h"
static struct session *main_session;
struct tmate_unpacker {
msgpack_object *argv;
int argc;
};
static void decoder_error(void)
{
#ifdef DEBUG
tmate_print_trace();
#endif
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_RAW)
decoder_error();
*len = uk->argv[0].via.raw.size;
*buf = uk->argv[0].via.raw.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 unpack_array(struct tmate_unpacker *uk,
struct tmate_unpacker *nested)
{
if (uk->argc == 0)
decoder_error();
init_unpacker(nested, uk->argv[0]);
uk->argv++;
uk->argc--;
}
#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--)
static void tmate_header(struct tmate_unpacker *uk)
{
int protocol = unpack_int(uk);
if (protocol != 1)
decoder_error();
tmate_debug("new master, protocol: %d", protocol);
}
extern u_int next_window_pane_id;
static void tmate_sync_window_panes(struct window *w,
struct tmate_unpacker *w_uk)
{
struct tmate_unpacker uk, tmp_uk;
struct window_pane *wp, *wp_tmp;
int active_pane_id;
TAILQ_FOREACH(wp, &w->panes, entry)
wp->flags |= PANE_KILL;
unpack_each(&uk, &tmp_uk, w_uk) {
int id = unpack_int(&uk);
int sx = unpack_int(&uk);
int sy = unpack_int(&uk);
int xoff = unpack_int(&uk);
int yoff = unpack_int(&uk);
wp = window_pane_find_by_id(id);
if (!wp) {
next_window_pane_id = id;
wp = window_add_pane(w, TMATE_HLIMIT);
window_set_active_pane(w, wp);
}
wp->flags &= ~PANE_KILL;
wp->xoff = xoff;
wp->yoff = yoff;
window_pane_resize(wp, sx, sy);
wp->flags |= PANE_REDRAW;
}
TAILQ_FOREACH_SAFE(wp, &w->panes, entry, wp_tmp) {
if (wp->flags & PANE_KILL)
window_remove_pane(w, wp);
}
active_pane_id = unpack_int(w_uk);
wp = window_pane_find_by_id(active_pane_id);
if (wp && wp->window == w)
window_set_active_pane(w, wp);
}
static void tmate_sync_window(struct tmate_unpacker *uk)
{
struct session *s;
struct winlink *wl;
struct window *w;
char *cause;
int id = unpack_int(uk);
char *name = unpack_string(uk);
int sx = unpack_int(uk);
int sy = unpack_int(uk);
if (!main_session) {
main_session = session_create("default", NULL, "default", NULL,
NULL, 0, sx, sy, &cause);
if (!main_session)
tmate_fatal("can't create main session");
}
s = main_session;
wl = winlink_find_by_index(&s->windows, id);
if (!wl) {
wl = session_new(s, name, "sh", NULL, id, &cause);
if (!wl)
tmate_fatal("can't create window id=%d", id);
session_select(s, RB_ROOT(&s->windows)->idx);
}
w = wl->window;
free(w->name);
w->name = name;
s->sx = w->sx = sx;
s->sy = w->sy = sy;
tmate_sync_window_panes(w, uk);
server_redraw_window(w);
}
static void tmate_pty_data(struct tmate_unpacker *uk)
{
struct window_pane *wp;
const char *buf;
size_t len;
int id;
id = unpack_int(uk);
unpack_raw(uk, &buf, &len);
wp = window_pane_find_by_id(id);
if (!wp)
tmate_fatal("can't find pane id=%d", id);
evbuffer_add(wp->event_input, buf, len);
input_parse(wp);
wp->window->flags |= WINDOW_SILENCE;
}
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_HEADER: tmate_header(uk); break;
case TMATE_SYNC_WINDOW: tmate_sync_window(uk); break;
case TMATE_PTY_DATA: tmate_pty_data(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");
}

39
tmate-encoder.c Normal file
View File

@ -0,0 +1,39 @@
#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)
{
encoder->buffer = evbuffer_new();
msgpack_packer_init(&encoder->pk, encoder, &msgpack_write);
}
#define pack(what, ...) msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__)
void tmate_client_key(int key)
{
pack(array, 2);
pack(int, TMATE_CLIENT_KEY);
pack(int, key);
}
void tmate_client_resize(u_int sx, u_int sy)
{
pack(array, 3);
pack(int, TMATE_CLIENT_RESIZE);
/* cast to signed, -1 == no clients */
pack(int, sx);
pack(int, sy);
}

164
tmate-server.c Normal file
View File

@ -0,0 +1,164 @@
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "tmate.h"
struct event_base *ev_base;
struct options global_options; /* server options */
struct options global_s_options; /* session options */
struct options global_w_options; /* window options */
struct environ global_environ;
struct event_base *ev_base;
char *cfg_file;
char *shell_cmd;
int debug_level;
time_t start_time;
char socket_path[MAXPATHLEN];
int login_shell;
char *environ_path;
pid_t environ_pid = -1;
int environ_session_id = -1;
void
setblocking(int fd, int state)
{
int mode;
if ((mode = fcntl(fd, F_GETFL)) != -1) {
if (!state)
mode |= O_NONBLOCK;
else
mode &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, mode);
}
}
const char*
get_full_path(const char *wd, const char *path)
{
static char newpath[MAXPATHLEN];
char oldpath[MAXPATHLEN];
if (getcwd(oldpath, sizeof oldpath) == NULL)
return (NULL);
if (chdir(wd) != 0)
return (NULL);
if (realpath(path, newpath) != 0)
return (NULL);
chdir(oldpath);
return (newpath);
}
static void usage(void)
{
fprintf(stderr, "usage: tmate-server [-p PORT]\n");
}
int main(int argc, char **argv)
{
int opt;
int port = 22;
char *log_path = NULL; /* stderr */
strcpy(socket_path, "/tmp/tmate-slave");
while ((opt = getopt(argc, argv, "p:l:v")) != -1) {
switch (opt) {
case 'p':
port = atoi(optarg);
break;
case 'l':
log_path = optarg;
break;
case 'v':
debug_level++;
break;
default:
usage();
return 1;
}
}
log_open(debug_level, log_path);
tmate_ssh_server_main(port);
return 0;
}
struct tmate_encoder *tmate_encoder;
void tmate_spawn_slave_server(struct tmate_ssh_client *client)
{
int quiet = 0;
int flags = IDENTIFY_UTF8 | IDENTIFY_256COLOURS;
struct tmate_encoder encoder;
struct tmate_decoder decoder;
tmate_debug("Spawn tmux slave server");
ev_base = osdep_event_init();
tmate_encoder_init(&encoder);
tmate_decoder_init(&decoder);
tmate_encoder = &encoder;
tmate_ssh_client_init(client, &encoder, &decoder);
environ_init(&global_environ);
options_init(&global_options, NULL);
options_table_populate_tree(server_options_table, &global_options);
options_set_number(&global_options, "quiet", quiet);
options_init(&global_s_options, NULL);
options_table_populate_tree(session_options_table, &global_s_options);
options_init(&global_w_options, NULL);
options_table_populate_tree(window_options_table, &global_w_options);
if (flags & IDENTIFY_UTF8) {
options_set_number(&global_s_options, "status-utf8", 1);
options_set_number(&global_s_options, "mouse-utf8", 1);
options_set_number(&global_w_options, "utf8", 1);
}
server_start(0, NULL);
/* never reached */
}
void tmate_spawn_slave_client(struct tmate_ssh_client *ssh_client)
{
struct tmate_ssh_client_pty _client;
struct tmate_ssh_client_pty *client = &_client;
int slave_pty;
int ret;
char *argv[] = {(char *)"attach", NULL};
client->session = ssh_client->session;
client->channel = ssh_client->channel;
client->winsize_pty = ssh_client->winsize_pty;
tmate_debug("Spawn tmux slave client");
ev_base = osdep_event_init();
if (openpty(&client->pty, &slave_pty, NULL, NULL, NULL) < 0)
tmate_fatal("Cannot allocate pty");
/* setsid(); */
/* ioctl(slave_pty, TIOCSCTTY, NULL); */
dup2(slave_pty, STDIN_FILENO);
dup2(slave_pty, STDOUT_FILENO);
stderr = stdout;
close(slave_pty);
tmate_ssh_client_pty_init(client);
ret = client_main(1, argv, IDENTIFY_UTF8 | IDENTIFY_256COLOURS);
tmate_flush_pty(client);
exit(ret);
}

143
tmate-ssh-client-pty.c Normal file
View File

@ -0,0 +1,143 @@
#include "tmate.h"
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
#include <errno.h>
static void consume_channel(struct tmate_ssh_client_pty *client)
{
ssize_t len, written;
char buf[4096];
char *ptr;
for (;;) {
len = ssh_channel_read_nonblocking(client->channel,
buf, sizeof(buf), 0);
if (len < 0) {
tmate_debug("Error reading from channel: %s",
ssh_get_error(client->session));
exit(1);
}
if (len == 0)
return;
ptr = buf;
setblocking(client->pty, 1);
while (len > 0) {
written = write(client->pty, ptr, len);
if (written < 0)
tmate_fatal("Error writing to pty");
ptr += written;
len -= written;
}
setblocking(client->pty, 0);
}
}
static void on_session_event(struct tmate_ssh_client_pty *client)
{
ssh_execute_message_callbacks(client->session);
consume_channel(client);
if (!ssh_is_connected(client->session)) {
tmate_debug("Disconnected");
exit(1);
}
}
static void __on_session_event(evutil_socket_t fd, short what, void *arg)
{
on_session_event(arg);
}
static int message_callback(struct tmate_ssh_client_pty *client,
ssh_message msg)
{
if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL &&
ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_WINDOW_CHANGE) {
struct winsize ws;
ws.ws_col = ssh_message_channel_request_pty_width(msg);
ws.ws_row = ssh_message_channel_request_pty_height(msg);
tmate_debug("change window size to %d, %d",
ws.ws_col, ws.ws_row);
ioctl(client->pty, TIOCSWINSZ, &ws);
kill(getpid(), SIGWINCH);
return 1;
}
return 0;
}
static int __message_callback(ssh_session session, ssh_message msg, void *arg)
{
return message_callback(arg, msg);
}
static void register_session_fd_event(struct tmate_ssh_client_pty *client)
{
ssh_set_message_callback(client->session, __message_callback, client);
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 on_pty_event(struct tmate_ssh_client_pty *client)
{
ssize_t len, written;
char buf[4096];
for (;;) {
len = read(client->pty, buf, sizeof(buf));
if (len < 0) {
if (errno == EAGAIN)
return;
tmate_fatal("pty read error");
}
if (len == 0)
tmate_fatal("pty reached EOF");
written = ssh_channel_write(client->channel, buf, len);
if (written < 0) {
tmate_debug("Error writing to channel: %s",
ssh_get_error(client->session));
exit(1);
}
if (len != written) {
tmate_fatal("Cannot write %d bytes, wrote %d",
(int)len, (int)written);
}
}
}
static void __on_pty_event(evutil_socket_t fd, short what, void *arg)
{
on_pty_event(arg);
}
void tmate_flush_pty(struct tmate_ssh_client_pty *client)
{
on_pty_event(client);
close(client->pty);
}
static void register_pty_event(struct tmate_ssh_client_pty *client)
{
setblocking(client->pty, 0);
event_assign(&client->ev_pty, ev_base, client->pty,
EV_READ | EV_PERSIST, __on_pty_event, client);
event_add(&client->ev_pty, NULL);
}
void tmate_ssh_client_pty_init(struct tmate_ssh_client_pty *client)
{
ioctl(client->pty, TIOCSWINSZ, &client->winsize_pty);
register_session_fd_event(client);
register_pty_event(client);
}

103
tmate-ssh-client.c Normal file
View File

@ -0,0 +1,103 @@
#include "tmate.h"
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
static void consume_channel(struct tmate_ssh_client *client)
{
char *buf;
ssize_t len;
for (;;) {
tmate_decoder_get_buffer(client->decoder, &buf, &len);
if (len == 0)
tmate_fatal("Input buffer full");
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));
exit(1);
}
if (len == 0)
break;
tmate_decoder_commit(client->decoder, len);
}
}
static void on_session_event(struct tmate_ssh_client *client)
{
ssh_session session = client->session;
ssh_channel channel = client->channel;
consume_channel(client);
if (!ssh_is_connected(session)) {
tmate_debug("Disconnected");
exit(1);
}
}
static void __on_session_event(evutil_socket_t fd, short what, void *arg)
{
on_session_event(arg);
}
static void register_session_fd_event(struct tmate_ssh_client *client)
{
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 flush_input_stream(struct tmate_ssh_client *client)
{
struct evbuffer *evb = client->encoder->buffer;
ssize_t len, written;
char *buf;
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));
exit(1);
}
evbuffer_drain(evb, written);
}
}
static void __flush_input_stream(evutil_socket_t fd, short what, void *arg)
{
flush_input_stream(arg);
}
static void register_input_stream_event(struct tmate_ssh_client *client)
{
event_assign(&client->encoder->ev_readable, ev_base, -1,
EV_READ | EV_PERSIST, __flush_input_stream, client);
event_add(&client->encoder->ev_readable, NULL);
}
void tmate_ssh_client_init(struct tmate_ssh_client *client,
struct tmate_encoder *encoder,
struct tmate_decoder *decoder)
{
client->winsize_pty.ws_col = 80;
client->winsize_pty.ws_row = 24;
client->encoder = encoder;
client->decoder = decoder;
register_session_fd_event(client);
register_input_stream_event(client);
}

225
tmate-ssh-server.c Normal file
View File

@ -0,0 +1,225 @@
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <event.h>
#include <sys/wait.h>
#include "tmate.h"
#define SSH_GRACE_PERIOD 60
static void client_bootstrap(struct tmate_ssh_client *client)
{
int auth = 0;
ssh_session session = client->session;
ssh_channel channel = NULL;
ssh_message msg;
int flag = 1;
setsockopt(ssh_get_fd(session), IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
alarm(SSH_GRACE_PERIOD);
tmate_debug("Exchanging DH keys");
if (ssh_handle_key_exchange(session) < 0)
tmate_fatal("Error doing the key exchange");
tmate_debug("Authenticating with public key");
while (!auth) {
msg = ssh_message_get(session);
if (!msg)
tmate_fatal("Authentification error");
switch (ssh_message_type(msg)) {
case SSH_REQUEST_AUTH:
switch (ssh_message_subtype(msg)) {
case SSH_AUTH_METHOD_PUBLICKEY:
if (ssh_message_auth_publickey_state(msg) == SSH_PUBLICKEY_STATE_NONE)
ssh_message_auth_reply_pk_ok_simple(msg);
else if (ssh_message_auth_publickey_state(msg) == SSH_PUBLICKEY_STATE_VALID) {
ssh_message_auth_reply_success(msg, 0);
auth = 1;
}
break;
case SSH_AUTH_METHOD_NONE:
default:
ssh_message_auth_set_methods(msg, SSH_AUTH_METHOD_PUBLICKEY);
ssh_message_reply_default(msg);
break;
}
break;
default:
ssh_message_reply_default(msg);
}
ssh_message_free(msg);
}
tmate_debug("Opening channel");
while (!channel) {
msg = ssh_message_get(session);
if (!msg)
tmate_fatal("Error getting channel");
if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL_OPEN &&
ssh_message_subtype(msg) == SSH_CHANNEL_SESSION) {
client->channel = channel = ssh_message_channel_request_open_reply_accept(msg);
if (!channel)
tmate_fatal("Error getting channel");
} else {
ssh_message_reply_default(msg);
}
ssh_message_free(msg);
}
tmate_debug("Getting client type");
while (1) {
msg = ssh_message_get(session);
if (!msg)
tmate_fatal("Error getting subsystem");
/* subsystem request */
if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL &&
ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_SUBSYSTEM &&
!strcmp(ssh_message_channel_request_subsystem(msg), "tmate")) {
alarm(0);
ssh_message_channel_request_reply_success(msg);
tmate_spawn_slave_server(client);
}
/* PTY request */
else if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL &&
ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_PTY) {
client->winsize_pty.ws_col = ssh_message_channel_request_pty_width(msg);
client->winsize_pty.ws_row = ssh_message_channel_request_pty_height(msg);
ssh_message_channel_request_reply_success(msg);
}
/* SHELL request */
else if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL &&
ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_SHELL) {
alarm(0);
ssh_message_channel_request_reply_success(msg);
tmate_spawn_slave_client(client);
}
/* Default */
else {
ssh_message_reply_default(msg);
}
ssh_message_free(msg);
}
}
static void handle_sigchld(void)
{
int status, child_dead, child_exit_status;
pid_t pid;
while ((pid = waitpid(0, &status, WNOHANG)) > 0) {
child_dead = 0;
if (WIFEXITED(status)) {
child_dead = 1;
child_exit_status = WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
child_dead = 1; child_exit_status = EXIT_FAILURE;
}
if (!child_dead)
continue;
tmate_debug("Child reaped pid=%d exit=%d", pid, child_exit_status);
} while (pid > 0);
}
static void handle_sigalrm(void)
{
log_fatal("Connection grace period (%d) passed", SSH_GRACE_PERIOD);
}
static void signal_handler(int sig)
{
switch (sig) {
case SIGCHLD: handle_sigchld(); break;
case SIGALRM: handle_sigalrm(); break;
}
}
static void setup_signals(void)
{
signal(SIGCHLD, signal_handler);
signal(SIGALRM, signal_handler);
}
static void ssh_log_cb(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 = ssh_log_cb
};
void tmate_ssh_server_main(int port)
{
struct tmate_ssh_client _client;
struct tmate_ssh_client *client = &_client;
ssh_bind bind;
pid_t pid;
int verbosity = SSH_LOG_NOLOG;
//int verbosity = SSH_LOG_PACKET;
setup_signals();
ssh_callbacks_init(&ssh_session_callbacks);
bind = ssh_bind_new();
if (!bind)
log_fatal("Cannot initialize ssh");
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT, &port);
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BANNER, SSH_BANNER);
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_DSAKEY, "keys/ssh_host_dsa_key");
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSAKEY, "keys/ssh_host_rsa_key");
if (ssh_bind_listen(bind) < 0)
log_fatal("Error listening to socket: %s\n", ssh_get_error(bind));
for (;;) {
client->session = ssh_new();
client->channel = NULL;
if (!client->session)
tmate_fatal("Cannot initialize session");
ssh_set_callbacks(client->session, &ssh_session_callbacks);
if (ssh_bind_accept(bind, client->session) < 0)
tmate_fatal("Error accepting connection: %s", ssh_get_error(bind));
if ((pid = fork()) < 0)
tmate_fatal("Can't fork");
if (pid) {
ssh_free(client->session);
} else {
ssh_bind_free(bind);
tmate_debug("Child spawned pid=%d", getpid());
client_bootstrap(client);
}
}
}

1
tmate.1 Symbolic link
View File

@ -0,0 +1 @@
tmux.1

110
tmate.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef TMATE_H
#define TMATE_H
#include <sys/types.h>
#include <msgpack.h>
#include <event.h>
#include "tmux.h"
#define tmate_debug(str, ...) log_debug("[tmate] " str, ##__VA_ARGS__)
#define tmate_warn(str, ...) log_warn("[tmate] " str, ##__VA_ARGS__)
#define tmate_info(str, ...) log_info("[tmate] " str, ##__VA_ARGS__)
#define tmate_fatal(str, ...) log_fatal("[tmate] " str, ##__VA_ARGS__)
/* tmate-encoder.c */
enum tmate_notifications {
TMATE_CLIENT_KEY,
TMATE_CLIENT_RESIZE,
};
struct tmate_encoder {
struct evbuffer *buffer;
struct event ev_readable;
msgpack_packer pk;
};
extern void tmate_encoder_init(struct tmate_encoder *encoder);
extern void tmate_write_header(void);
extern void tmate_write_pane(int pane, const char *data, size_t size);
extern void tmate_client_key(int key);
extern void tmate_client_resize(u_int sx, u_int sy);
/* tmate-decoder.c */
#define TMATE_HLIMIT 1000
#define TMATE_MAX_MESSAGE_SIZE (16*1024)
enum tmate_commands {
TMATE_HEADER,
TMATE_SYNC_WINDOW,
TMATE_PTY_DATA,
};
#define TMATE_PANE_ACTIVE 1
struct tmate_decoder {
struct msgpack_unpacker unpacker;
};
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;
struct tmate_ssh_client {
ssh_session session;
ssh_channel channel;
struct tmate_encoder *encoder;
struct tmate_decoder *decoder;
struct winsize winsize_pty;
struct event ev_ssh;
};
extern void tmate_ssh_client_init(struct tmate_ssh_client *ssh_client,
struct tmate_encoder *encoder,
struct tmate_decoder *decoder);
/* tmate-ssh-client-pty.c */
struct tmate_ssh_client_pty {
ssh_session session;
ssh_channel channel;
int pty;
struct winsize winsize_pty;
struct event ev_ssh;
struct event ev_pty;
};
extern void tmate_ssh_client_pty_init(struct tmate_ssh_client_pty *client);
extern void tmate_flush_pty(struct tmate_ssh_client_pty *client);
/* tmate-ssh-server.c */
#define SSH_BANNER "tmate"
extern void tmate_ssh_server_main(int port);
/* tmate-server.c */
extern struct tmate_encoder *tmate_encoder;
extern void tmate_spawn_slave_server(struct tmate_ssh_client *client);
extern void tmate_spawn_slave_client(struct tmate_ssh_client *client);
/* tmate-debug.c */
extern void tmate_print_trace(void);
#endif

11
tmux.h
View File

@ -19,6 +19,8 @@
#ifndef TMUX_H
#define TMUX_H
#define TMATE_SLAVE
#define PROTOCOL_VERSION 7
#include <sys/param.h>
@ -950,8 +952,13 @@ struct window_pane {
struct event changes_timer;
u_int changes_redraw;
#ifdef TMATE_SLAVE
#define PANE_KILL 0x10
struct evbuffer *event_input;
#else
int fd;
struct bufferevent *event;
#endif
struct input_ctx ictx;
@ -1714,8 +1721,10 @@ int paste_free_index(struct paste_stack *, u_int);
void paste_add(struct paste_stack *, char *, size_t, u_int);
int paste_replace(struct paste_stack *, u_int, char *, size_t);
char *paste_print(struct paste_buffer *, size_t);
#ifndef TMATE_SLAVE
void paste_send_pane(struct paste_buffer *, struct window_pane *,
const char *, int);
#endif
/* clock.c */
extern const char clock_table[14][5][5];
@ -1967,10 +1976,12 @@ void input_init(struct window_pane *);
void input_free(struct window_pane *);
void input_parse(struct window_pane *);
#ifndef TMATE_SLAVE
/* input-key.c */
void input_key(struct window_pane *, int);
void input_mouse(struct window_pane *, struct session *,
struct mouse_event *);
#endif
/* xterm-keys.c */
char *xterm_keys_lookup(int);

View File

@ -26,6 +26,7 @@
#include <unistd.h>
#include "tmux.h"
#include "tmate.h"
/*
* Handle keys input from the outside terminal. tty_default_*_keys[] are a base
@ -301,10 +302,10 @@ tty_keys_add(struct tty *tty, const char *s, int key)
keystr = key_string_lookup_key(key);
if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) {
log_debug("new key %s: 0x%x (%s)", s, key, keystr);
log_debug2("new key %s: 0x%x (%s)", s, key, keystr);
tty_keys_add1(&tty->key_tree, s, key);
} else {
log_debug("replacing key %s: 0x%x (%s)", s, key, keystr);
log_debug2("replacing key %s: 0x%x (%s)", s, key, keystr);
tk->key = key;
}
}
@ -455,7 +456,7 @@ tty_keys_next(struct tty *tty)
len = EVBUFFER_LENGTH(tty->event->input);
if (len == 0)
return (0);
log_debug("keys are %zu (%.*s)", len, (int) len, buf);
log_debug2("keys are %zu (%.*s)", len, (int) len, buf);
/* Is this device attributes response? */
switch (tty_keys_device(tty, buf, len, &size)) {
@ -535,7 +536,7 @@ first_key:
goto complete_key;
partial_key:
log_debug("partial key %.*s", (int) len, buf);
log_debug2("partial key %.*s", (int) len, buf);
/* If timer is going, check for expiration. */
if (tty->flags & TTY_TIMER) {
@ -562,7 +563,7 @@ partial_key:
return (0);
complete_key:
log_debug("complete key %.*s %#x", (int) size, buf, key);
log_debug2("complete key %.*s %#x", (int) size, buf, key);
/* Remove data from buffer. */
evbuffer_drain(tty->event->input, size);
@ -673,7 +674,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
else
y = value;
}
log_debug("mouse input: %.*s", (int) *size, buf);
log_debug2("mouse input: %.*s", (int) *size, buf);
/* Check and return the mouse input. */
if (b < 32 || x < 33 || y < 33)
@ -714,7 +715,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
return (-1);
y = 10 * y + (c - '0');
}
log_debug("mouse input (sgr): %.*s", (int) *size, buf);
log_debug2("mouse input (sgr): %.*s", (int) *size, buf);
/* Check and return the mouse input. */
if (x < 1 || y < 1)
@ -824,7 +825,7 @@ tty_keys_device(struct tty *tty, const char *buf, size_t len, size_t *size)
if (*endptr != ';')
class = 0;
log_debug("received service class %u", class);
log_debug2("received service class %u", class);
tty_set_class(tty, class);
return (0);

1
tty.c
View File

@ -30,6 +30,7 @@
#include <unistd.h>
#include "tmux.h"
#include "tmate.h"
void tty_read_callback(struct bufferevent *, void *);
void tty_error_callback(struct bufferevent *, short, void *);

View File

@ -178,8 +178,10 @@ window_copy_init(struct window_pane *wp)
data->searchtype = WINDOW_COPY_OFF;
data->searchstr = NULL;
#ifndef TMATE_SLAVE
if (wp->fd != -1)
bufferevent_disable(wp->event, EV_READ|EV_WRITE);
#endif
data->jumptype = WINDOW_COPY_OFF;
data->jumpchar = '\0';
@ -241,8 +243,10 @@ window_copy_free(struct window_pane *wp)
{
struct window_copy_mode_data *data = wp->modedata;
#ifndef TMATE_SLAVE
if (wp->fd != -1)
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
#endif
free(data->searchstr);
free(data->inputstr);

View File

@ -674,8 +674,12 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->shell = NULL;
wp->cwd = NULL;
#ifdef TMATE_SLAVE
wp->event_input = evbuffer_new();
#else
wp->fd = -1;
wp->event = NULL;
#endif
wp->mode = NULL;
@ -709,10 +713,14 @@ window_pane_destroy(struct window_pane *wp)
if (event_initialized(&wp->changes_timer))
evtimer_del(&wp->changes_timer);
#ifdef TMATE_SLAVE
evbuffer_free(wp->event_input);
#else
if (wp->fd != -1) {
bufferevent_free(wp->event);
close(wp->fd);
}
#endif
input_free(wp);
@ -737,6 +745,9 @@ int
window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
const char *cwd, struct environ *env, struct termios *tio, char **cause)
{
#ifdef TMATE_SLAVE
return 0;
#else
struct winsize ws;
char *argv0, paneid[16];
const char *ptr;
@ -824,6 +835,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
return (0);
#endif
}
void
@ -869,16 +881,21 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data)
struct window_pane *wp = data;
char *new_data;
size_t new_size;
#ifdef TMATE_SLAVE
struct evbuffer *evb = wp->event_input;
#else
struct evbuffer *evb = wp->event->input;
#endif
new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off;
new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off;
if (wp->pipe_fd != -1 && new_size > 0) {
new_data = EVBUFFER_DATA(wp->event->input);
new_data = EVBUFFER_DATA(evb);
bufferevent_write(wp->pipe_event, new_data, new_size);
}
input_parse(wp);
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
wp->pipe_off = EVBUFFER_LENGTH(evb);
/*
* If we get here, we're not outputting anymore, so set the silence
@ -1022,6 +1039,7 @@ window_pane_reset_mode(struct window_pane *wp)
wp->flags |= PANE_REDRAW;
}
#ifndef TMATE_SLAVE
void
window_pane_key(struct window_pane *wp, struct session *sess, int key)
{
@ -1070,14 +1088,17 @@ window_pane_mouse(
} else if (wp->fd != -1)
input_mouse(wp, sess, m);
}
#endif
int
window_pane_visible(struct window_pane *wp)
{
struct window *w = wp->window;
#ifndef TMATE_SLAVE
if (wp->layout_cell == NULL)
return (0);
#endif
if (wp->xoff >= w->sx || wp->yoff >= w->sy)
return (0);
if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)