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:
parent
1b083aa0fd
commit
dbc4ecc92d
4
.gitignore
vendored
4
.gitignore
vendored
@ -15,3 +15,7 @@ tmux
|
||||
Makefile
|
||||
Makefile.in
|
||||
configure
|
||||
keys/
|
||||
tmate-server
|
||||
cscope.*
|
||||
tags
|
||||
|
64
Makefile.am
64
Makefile.am
@ -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
|
||||
|
13
client.c
13
client.c
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,9 +116,7 @@ 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;
|
||||
shell = _PATH_BSHELL;
|
||||
|
||||
if ((lc = layout_split_pane(wp, type, size, 0)) == NULL) {
|
||||
cause = xstrdup("pane too small");
|
||||
@ -165,4 +166,5 @@ error:
|
||||
cmdq_error(cmdq, "create pane failed: %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
#endif
|
||||
}
|
||||
|
4
cmd.c
4
cmd.c
@ -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
|
||||
}
|
||||
|
@ -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
13
create_keys.sh
Executable 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
|
4
format.c
4
format.c
@ -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);
|
||||
|
@ -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
62
input.c
@ -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);
|
||||
|
||||
|
14
log.c
14
log.c
@ -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 (log_file == NULL)
|
||||
return;
|
||||
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
18
names.c
@ -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
|
||||
|
2
paste.c
2
paste.c
@ -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
|
||||
|
7
resize.c
7
resize.c
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,10 +1007,7 @@ 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;
|
||||
shell = _PATH_BSHELL;
|
||||
if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
|
||||
strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
|
||||
|
||||
|
@ -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);
|
||||
|
13
server.c
13
server.c
@ -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();
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "tmux.h"
|
||||
#include "tmate.h"
|
||||
|
||||
/* Global session list. */
|
||||
struct sessions sessions;
|
||||
@ -244,9 +245,7 @@ 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;
|
||||
shell = _PATH_BSHELL;
|
||||
|
||||
hlimit = options_get_number(&s->options, "history-limit");
|
||||
w = window_create(
|
||||
|
76
tmate-debug.c
Normal file
76
tmate-debug.c
Normal 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
260
tmate-decoder.c
Normal 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
39
tmate-encoder.c
Normal 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
164
tmate-server.c
Normal 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
143
tmate-ssh-client-pty.c
Normal 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
103
tmate-ssh-client.c
Normal 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
225
tmate-ssh-server.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
110
tmate.h
Normal file
110
tmate.h
Normal 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
11
tmux.h
@ -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);
|
||||
|
17
tty-keys.c
17
tty-keys.c
@ -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
1
tty.c
@ -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 *);
|
||||
|
@ -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);
|
||||
|
27
window.c
27
window.c
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user