diff --git a/Makefile.am b/Makefile.am index beff8484..fa327d6c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,6 +181,7 @@ dist_tmate_SOURCES = \ tmate-ssh-client.c \ tmate-encoder.c \ tmate-decoder.c \ + tmate-msg.c \ tmate.c \ tmux.c \ tty-acs.c \ diff --git a/options-table.c b/options-table.c index 76a61619..7d0119e9 100644 --- a/options-table.c +++ b/options-table.c @@ -169,6 +169,13 @@ const struct options_table_entry session_options_table[] = { .default_num = 750 }, + { .name = "tmate-display-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 30000 + }, + { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, diff --git a/resize.c b/resize.c index 7f2ed236..91a8fa97 100644 --- a/resize.c +++ b/resize.c @@ -62,6 +62,10 @@ recalculate_sizes(void) if (c == NULL || c->flags & CLIENT_SUSPENDED) continue; if (c->session == s) { +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) + has_status = 1; +#endif if (c->tty.sx < ssx) ssx = c->tty.sx; if (has_status && diff --git a/server-client.c b/server-client.c index 89a4faa6..1a7e3ca0 100644 --- a/server-client.c +++ b/server-client.c @@ -271,6 +271,9 @@ server_client_status_timer(void) s = c->session; if (!options_get_number(&s->options, "status")) +#ifdef TMATE + if (!(c->flags & CLIENT_FORCE_STATUS)) +#endif continue; interval = options_get_number(&s->options, "status-interval"); @@ -395,6 +398,9 @@ server_client_handle_key(struct client *c, int key) /* Handle status line. */ if (!(c->flags & CLIENT_READONLY)) { +#ifdef TMATE + if (!(c->flags & CLIENT_FORCE_STATUS)) +#endif status_message_clear(c); server_clear_identify(c); } @@ -635,6 +641,10 @@ server_client_reset_state(struct client *c) tty_region(&c->tty, 0, c->tty.sy - 1); status = options_get_number(oo, "status"); +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) + status = 1; +#endif if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) tty_cursor(&c->tty, 0, 0); else { diff --git a/status.c b/status.c index f7a3420e..3eaa933d 100644 --- a/status.c +++ b/status.c @@ -171,6 +171,9 @@ status_redraw(struct client *c) /* No status line? */ if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) +#ifdef TMATE + if (c->tty.sy == 0 || !(c->flags & CLIENT_FORCE_STATUS)) +#endif return (1); left = right = NULL; larrow = rarrow = 0; @@ -802,6 +805,7 @@ status_message_set(struct client *c, const char *fmt, ...) } } + /* FIXME tmux: session can be NULL */ delay = options_get_number(&c->session->options, "display-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; @@ -826,6 +830,12 @@ status_message_clear(struct client *c) c->message_string = NULL; c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) { + c->flags &= ~CLIENT_FORCE_STATUS; + recalculate_sizes(); + } +#endif c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ screen_reinit(&c->status); diff --git a/tmate-decoder.c b/tmate-decoder.c index 1abe700e..00c4890f 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -74,6 +74,13 @@ static char *unpack_string(struct tmate_unpacker *uk) return alloc_buf; } +static void tmate_reply_header(struct tmate_unpacker *uk) +{ + unsigned long flags = unpack_int(uk); + char *remote_session = unpack_string(uk); + + tmate_status_message("Remote session: %s", remote_session); +} static void tmate_client_pane_key(struct tmate_unpacker *uk) { @@ -138,6 +145,7 @@ static void handle_message(msgpack_object obj) init_unpacker(uk, obj); switch (unpack_int(uk)) { + case TMATE_REPLY_HEADER: tmate_reply_header(uk); break; case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; case TMATE_CLIENT_CMD: tmate_client_cmd(uk); break; diff --git a/tmate-msg.c b/tmate-msg.c new file mode 100644 index 00000000..7fe0c0b8 --- /dev/null +++ b/tmate-msg.c @@ -0,0 +1,78 @@ +#include +#include "tmate.h" + +void status_message_callback(int, short, void *); + +/* Very similar to status.c:status_message_set */ + +static void tmate_status_message_client(struct client *c, const char *message) +{ + struct timeval tv; + struct session *s = c->session; + struct message_entry *msg; + int delay; + u_int i, limit; + + status_prompt_clear(c); + status_message_clear(c); + + xasprintf(&c->message_string, "tmate: %s", message); + + ARRAY_EXPAND(&c->message_log, 1); + msg = &ARRAY_LAST(&c->message_log); + msg->msg_time = time(NULL); + msg->msg = xstrdup(c->message_string); + + if (!s) + return; + + limit = options_get_number(&s->options, "message-limit"); + if (ARRAY_LENGTH(&c->message_log) > limit) { + limit = ARRAY_LENGTH(&c->message_log) - limit; + for (i = 0; i < limit; i++) { + msg = &ARRAY_FIRST(&c->message_log); + free(msg->msg); + ARRAY_REMOVE(&c->message_log, 0); + } + } + + delay = options_get_number(&c->session->options, "tmate-display-time"); + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + + if (event_initialized (&c->message_timer)) + evtimer_del(&c->message_timer); + evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); + + c->flags |= CLIENT_STATUS | CLIENT_FORCE_STATUS; + + recalculate_sizes(); +} + +void __tmate_status_message(const char *fmt, va_list ap) +{ + struct client *c; + unsigned int i; + char *message; + + xvasprintf(&message, fmt, ap); + tmate_debug("%s", message); + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c && !(c->flags & CLIENT_READONLY)) + tmate_status_message_client(c, message); + } + + free(message); +} + +void printflike1 tmate_status_message(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __tmate_status_message(fmt, ap); + va_end(ap); +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index ccb147ed..4ca1a914 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -11,8 +11,10 @@ static void consume_channel(struct tmate_ssh_client *client); static void flush_input_stream(struct tmate_ssh_client *client); static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); static void __on_session_event(evutil_socket_t fd, short what, void *arg); -static void disconnect_session(struct tmate_ssh_client *client); -static void reconnect_session(struct tmate_ssh_client *client); +static void printflike2 disconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...); +static void printflike2 reconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...); static void log_function(ssh_session session, int priority, const char *message, void *userdata) @@ -56,9 +58,8 @@ static void consume_channel(struct tmate_ssh_client *client) tmate_decoder_get_buffer(client->decoder, &buf, &len); len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); if (len < 0) { - tmate_debug("Error reading from channel: %s", - ssh_get_error(client->session)); - reconnect_session(client); + reconnect_session(client, "Error reading from channel: %s", + ssh_get_error(client->session)); break; } @@ -107,7 +108,7 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); - tmate_debug("Connecting..."); + tmate_status_message("Connecting to %s...", TMATE_HOST); client->state = SSH_CONNECT; /* fall through */ @@ -117,8 +118,8 @@ static void on_session_event(struct tmate_ssh_client *client) register_session_fd_event(client); return; case SSH_ERROR: - tmate_debug("Error connecting: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Error connecting: %s", + ssh_get_error(session)); return; case SSH_OK: register_session_fd_event(client); @@ -129,8 +130,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_SERVER: if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { - tmate_debug("Cannnot authenticate server"); - disconnect_session(client); + disconnect_session(client, "Cannnot authenticate server"); return; } @@ -165,8 +165,7 @@ static void on_session_event(struct tmate_ssh_client *client) free(hash_str); if (!match) { - tmate_debug("Cannnot authenticate server"); - disconnect_session(client); + disconnect_session(client, "Cannnot authenticate server"); return; } @@ -180,12 +179,11 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - tmate_debug("Access denied. Try again later."); - disconnect_session(client); + disconnect_session(client, "Access denied. Try again later."); return; case SSH_AUTH_ERROR: - tmate_debug("Auth error: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Auth error: %s", + ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: tmate_debug("Auth successful"); @@ -198,8 +196,8 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - tmate_debug("Error opening channel: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Error opening channel: %s", + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Session opened, initalizing tmate"); @@ -212,8 +210,8 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - tmate_debug("Error initializing tmate: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Error initializing tmate: %s", + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Ready"); @@ -230,8 +228,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_READY: consume_channel(client); if (!ssh_is_connected(session)) { - tmate_debug("Disconnected"); - reconnect_session(client); + reconnect_session(client, "Disconnected"); return; } } @@ -255,9 +252,8 @@ static void flush_input_stream(struct tmate_ssh_client *client) written = ssh_channel_write(client->channel, buf, len); if (written < 0) { - tmate_debug("Error writing to channel: %s", - ssh_get_error(client->session)); - reconnect_session(client); + reconnect_session(client, "Error writing to channel: %s", + ssh_get_error(client->session)); return; } @@ -275,8 +271,11 @@ static void __on_session_event(evutil_socket_t fd, short what, void *arg) on_session_event(arg); } -static void disconnect_session(struct tmate_ssh_client *client) +static void __disconnect_session(struct tmate_ssh_client *client, + const char *fmt, va_list va) { + __tmate_status_message(fmt, va); + if (event_initialized(&client->ev_ssh)) { event_del(&client->ev_ssh); client->ev_ssh.ev_flags = 0; @@ -297,6 +296,16 @@ static void disconnect_session(struct tmate_ssh_client *client) client->state = SSH_NONE; } +static void printflike2 disconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __disconnect_session(client, fmt, ap); + va_end(ap); +} + static void connect_session(struct tmate_ssh_client *client) { if (!client->session) { @@ -310,11 +319,15 @@ static void on_reconnect_timer(evutil_socket_t fd, short what, void *arg) connect_session(arg); } -static void reconnect_session(struct tmate_ssh_client *client) +static void printflike2 reconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...) { struct timeval tv; + va_list ap; - disconnect_session(client); + va_start(ap, fmt); + __disconnect_session(client, fmt, ap); + va_end(ap); /* Not yet implemented... */ #if 0 diff --git a/tmate.h b/tmate.h index c9848045..612fc184 100644 --- a/tmate.h +++ b/tmate.h @@ -44,6 +44,7 @@ extern void tmate_status(const char *left, const char *right); /* tmate-decoder.c */ enum tmate_client_commands { + TMATE_REPLY_HEADER, TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, TMATE_CLIENT_CMD, @@ -112,4 +113,9 @@ extern void tmate_client_start(void); /* tmate-debug.c */ extern void tmate_print_trace (void); +/* tmate-msg.c */ + +extern void __tmate_status_message(const char *fmt, va_list ap); +extern void printflike1 tmate_status_message(const char *fmt, ...); + #endif diff --git a/tmux.h b/tmux.h index c8afa93e..7a3cbb3e 100644 --- a/tmux.h +++ b/tmux.h @@ -1332,6 +1332,9 @@ struct client { #define CLIENT_REDRAWWINDOW 0x1000 #define CLIENT_CONTROL 0x2000 #define CLIENT_FOCUSED 0x4000 +#ifdef TMATE +#define CLIENT_FORCE_STATUS 0x800000 +#endif int flags; struct event identify_timer;