diff --git a/Makefile.am b/Makefile.am index 09efd142..b4a0679a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -179,8 +179,9 @@ dist_tmate_slave_SOURCES = \ signal.c \ status.c \ tmate-debug.c \ - tmate-decoder.c \ - tmate-encoder.c \ + tmate-client-decoder.c \ + tmate-client-encoder.c \ + tmate-msgpack.c \ tmate-slave.c \ tmate-ssh-client-pty.c \ tmate-ssh-client.c \ diff --git a/client.c b/client.c index c94cbb22..b52798bc 100644 --- a/client.c +++ b/client.c @@ -207,7 +207,7 @@ client_main(int argc, char **argv, int flags) } #ifdef TMATE_SLAVE - fd = tmux_socket_fd; + fd = tmate_session->tmux_socket_fd; #else /* @@ -325,7 +325,7 @@ client_send_identify(int flags) int fd; #ifdef TMATE_SLAVE - strcpy(data.ip_address, tmate_client.ip_address); + strcpy(data.ip_address, tmate_session->ssh_client.ip_address); #endif data.flags = flags; diff --git a/log.c b/log.c index 67cc1b1a..5228e735 100644 --- a/log.c +++ b/log.c @@ -73,11 +73,13 @@ log_vwrite(int level, const char *msg, va_list ap) { char *fmt = NULL; + const char *token = tmate_session->session_token; + if (log_settings.log_level < level) return; - if (tmate_session_token) { - if (asprintf(&fmt, "[%s] %s", tmate_session_token, msg) < 0) + if (token) { + if (asprintf(&fmt, "[%s] %s", token, msg) < 0) exit(1); msg = fmt; } diff --git a/server.c b/server.c index 5131e1c1..7701be1d 100644 --- a/server.c +++ b/server.c @@ -162,7 +162,7 @@ server_start(int lockfd, char *lockfile) #endif #ifdef TMATE_SLAVE - server_fd = tmux_socket_fd; + server_fd = tmate_session->tmux_socket_fd; #else server_fd = server_create_socket(); server_client_create(pair[1]); diff --git a/tmate-decoder.c b/tmate-client-decoder.c similarity index 61% rename from tmate-decoder.c rename to tmate-client-decoder.c index 64b63719..45958ae7 100644 --- a/tmate-decoder.c +++ b/tmate-client-decoder.c @@ -6,111 +6,21 @@ char *tmate_left_status, *tmate_right_status; 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_decoder *decoder, +static void tmate_header(struct tmate_session *session, struct tmate_unpacker *uk) { char port_arg[16] = {0}; char *client_version = xstrdup("< 1.8.6"); char tmp[512]; - decoder->protocol = unpack_int(uk); - if (decoder->protocol >= 3) { + session->client_protocol_version = unpack_int(uk); + if (session->client_protocol_version >= 3) { free(client_version); client_version = unpack_string(uk); } tmate_debug("new master, client version: %s, protocol version: %d", - client_version, decoder->protocol); + client_version, session->client_protocol_version); #if 0 if (strcmp(client_version, TMATE_LATEST_VERSION)) @@ -119,14 +29,14 @@ static void tmate_header(struct tmate_decoder *decoder, free(client_version); - if (tmate_settings.ssh_port != 22) - sprintf(port_arg, " -p%d", tmate_settings.ssh_port); + if (tmate_settings->ssh_port != 22) + sprintf(port_arg, " -p%d", tmate_settings->ssh_port); - sprintf(tmp, "ssh%s ro-%s@%s", port_arg, tmate_session_token_ro, tmate_settings.tmate_host); + sprintf(tmp, "ssh%s ro-%s@%s", port_arg, session->session_token_ro, tmate_settings->tmate_host); tmate_notify("Remote session read only: %s (clear your screen if you share this)", tmp); tmate_send_env("tmate_ssh_ro", tmp); - sprintf(tmp, "ssh%s %s@%s", port_arg, tmate_session_token, tmate_settings.tmate_host); + sprintf(tmp, "ssh%s %s@%s", port_arg, session->session_token, tmate_settings->tmate_host); tmate_notify("Remote session: %s", tmp); tmate_send_env("tmate_ssh", tmp); @@ -231,7 +141,7 @@ static void tmate_sync_windows(struct session *s, server_redraw_window(wl->window); } -static void tmate_sync_layout(struct tmate_decoder *decoder, +static void tmate_sync_layout(struct tmate_session *session, struct tmate_unpacker *uk) { struct session *s; @@ -254,7 +164,7 @@ static void tmate_sync_layout(struct tmate_decoder *decoder, tmate_sync_windows(s, uk); } -static void tmate_pty_data(struct tmate_decoder *decoder, +static void tmate_pty_data(struct tmate_session *session, struct tmate_unpacker *uk) { struct window_pane *wp; @@ -263,7 +173,7 @@ static void tmate_pty_data(struct tmate_decoder *decoder, int id; id = unpack_int(uk); - unpack_raw(uk, &buf, &len); + unpack_buffer(uk, &buf, &len); wp = window_pane_find_by_id(id); if (!wp) @@ -275,7 +185,7 @@ static void tmate_pty_data(struct tmate_decoder *decoder, wp->window->flags |= WINDOW_SILENCE; } -static void tmate_exec_cmd(struct tmate_decoder *decoder, +static void tmate_exec_cmd(struct tmate_session *session, struct tmate_unpacker *uk) { struct cmd_q *cmd_q; @@ -297,7 +207,7 @@ out: free(cmd_str); } -static void tmate_failed_cmd(struct tmate_decoder *decoder, +static void tmate_failed_cmd(struct tmate_session *session, struct tmate_unpacker *uk) { struct client *c; @@ -320,7 +230,7 @@ static void tmate_failed_cmd(struct tmate_decoder *decoder, free(cause); } -static void tmate_status(struct tmate_decoder *decoder, +static void tmate_status(struct tmate_session *session, struct tmate_unpacker *uk) { struct client *c; @@ -342,7 +252,7 @@ extern void window_copy_redraw_screen(struct window_pane *); extern int window_copy_update_selection(struct window_pane *); extern void window_copy_init_for_output(struct window_pane *); -static void tmate_sync_copy_mode(struct tmate_decoder *decoder, +static void tmate_sync_copy_mode(struct tmate_session *session, struct tmate_unpacker *uk) { struct tmate_unpacker cm_uk, sel_uk, input_uk; @@ -368,7 +278,7 @@ static void tmate_sync_copy_mode(struct tmate_decoder *decoder, return; } - if (decoder->protocol >= 2) + if (session->client_protocol_version >= 2) base_backing = unpack_int(&cm_uk); if (window_pane_set_mode(wp, &window_copy_mode) == 0) { @@ -388,7 +298,7 @@ static void tmate_sync_copy_mode(struct tmate_decoder *decoder, if (sel_uk.argc) { data->screen.sel.flag = 1; data->selx = unpack_int(&sel_uk); - if (decoder->protocol >= 2) { + if (session->client_protocol_version >= 2) { data->sely = -unpack_int(&sel_uk) + screen_hsize(data->backing) + screen_size_y(data->backing) - 1; @@ -424,7 +334,7 @@ static void tmate_sync_copy_mode(struct tmate_decoder *decoder, window_copy_redraw_screen(wp); } -static void tmate_write_copy_mode(struct tmate_decoder *decoder, +static void tmate_write_copy_mode(struct tmate_session *session, struct tmate_unpacker *uk) { struct window_pane *wp; @@ -445,70 +355,21 @@ static void tmate_write_copy_mode(struct tmate_decoder *decoder, free(str); } -static void handle_message(struct tmate_decoder *decoder, msgpack_object obj) +void tmate_dispatch_daemon_message(struct tmate_session *session, + struct tmate_unpacker *uk) { - struct tmate_unpacker _uk; - struct tmate_unpacker *uk = &_uk; - int cmd; - - init_unpacker(uk, obj); - - cmd = unpack_int(uk); - -#if 0 - /* Really verbose tracers */ - if (cmd != TMATE_PTY_DATA) { - msgpack_object_print(stderr, obj); - fprintf(stderr, "\n"); - } -#endif +#define dispatch(c, f) case c: f(session, uk); break + int cmd = unpack_int(uk); switch (cmd) { - case TMATE_HEADER: tmate_header(decoder, uk); break; - case TMATE_SYNC_LAYOUT: tmate_sync_layout(decoder, uk); break; - case TMATE_PTY_DATA: tmate_pty_data(decoder, uk); break; - case TMATE_EXEC_CMD: tmate_exec_cmd(decoder, uk); break; - case TMATE_FAILED_CMD: tmate_failed_cmd(decoder, uk); break; - case TMATE_STATUS: tmate_status(decoder, uk); break; - case TMATE_SYNC_COPY_MODE: tmate_sync_copy_mode(decoder, uk); break; - case TMATE_WRITE_COPY_MODE: tmate_write_copy_mode(decoder, uk); break; - default: decoder_error(); + dispatch(TMATE_HEADER, tmate_header); + dispatch(TMATE_SYNC_LAYOUT, tmate_sync_layout); + dispatch(TMATE_PTY_DATA, tmate_pty_data); + dispatch(TMATE_EXEC_CMD, tmate_exec_cmd); + dispatch(TMATE_FAILED_CMD, tmate_failed_cmd); + dispatch(TMATE_STATUS, tmate_status); + dispatch(TMATE_SYNC_COPY_MODE, tmate_sync_copy_mode); + dispatch(TMATE_WRITE_COPY_MODE, tmate_write_copy_mode); + default: tmate_fatal("Bad message type: %d", cmd); } } - -void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) -{ - msgpack_unpacked result; - - msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); - - msgpack_unpacked_init(&result); - while (msgpack_unpacker_next(&decoder->unpacker, &result)) { - handle_message(decoder, result.data); - } - msgpack_unpacked_destroy(&result); - - if (msgpack_unpacker_message_size(&decoder->unpacker) > - TMATE_MAX_MESSAGE_SIZE) { - tmate_fatal("Message too big"); - } -} - -void tmate_decoder_get_buffer(struct tmate_decoder *decoder, - char **buf, size_t *len) -{ - /* rewind the buffer if possible */ - if (msgpack_unpacker_buffer_capacity(&decoder->unpacker) < - TMATE_MAX_MESSAGE_SIZE) { - msgpack_unpacker_expand_buffer(&decoder->unpacker, 0); - } - - *buf = msgpack_unpacker_buffer(&decoder->unpacker); - *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); -} - -void tmate_decoder_init(struct tmate_decoder *decoder) -{ - if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE)) - tmate_fatal("cannot initialize the unpacker"); -} diff --git a/tmate-encoder.c b/tmate-client-encoder.c similarity index 74% rename from tmate-encoder.c rename to tmate-client-encoder.c index 6a5315c1..416015cc 100644 --- a/tmate-encoder.c +++ b/tmate-client-encoder.c @@ -1,36 +1,6 @@ #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 msgpack_pack_string(pk, str) do { \ - int __strlen = strlen(str); \ - msgpack_pack_raw(pk, __strlen); \ - msgpack_pack_raw_body(pk, str, __strlen); \ -} while(0) - -/* tmate_encoder may be NULL when replaying a session */ -#define pack(what, ...) do { \ - if (tmate_encoder) \ - msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__); \ -} while(0) +#define pack(what, ...) _pack(&tmate_session->client_encoder, what, __VA_ARGS__) static void __tmate_notify(const char *msg) { @@ -75,9 +45,9 @@ void printflike2 tmate_notify_later(int timeout, const char *fmt, ...) * multiple times. */ - evtimer_assign(&tmate_encoder->ev_notify_timer, ev_base, + evtimer_assign(&tmate_session->ev_notify_timer, ev_base, __tmate_notify_later, msg); - evtimer_add(&tmate_encoder->ev_notify_timer, &tv); + evtimer_add(&tmate_session->ev_notify_timer, &tv); } static int num_clients(void) @@ -127,7 +97,7 @@ void tmate_notify_client_left(struct client *c) void tmate_send_client_ready(void) { - if (tmate_decoder && tmate_decoder->protocol < 4) + if (tmate_session->client_protocol_version < 4) return; pack(array, 1); @@ -136,7 +106,7 @@ void tmate_send_client_ready(void) void tmate_send_env(const char *name, const char *value) { - if (tmate_decoder && tmate_decoder->protocol < 4) + if (tmate_session->client_protocol_version < 4) return; pack(array, 3); diff --git a/tmate-debug.c b/tmate-debug.c index 103a2e25..1325fb1e 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -52,7 +52,7 @@ static int print_resolved_stack_frame(const char *frame) } #endif -void tmate_print_trace(void) +void tmate_print_stack_trace(void) { void *array[20]; size_t size; diff --git a/tmate-msgpack.c b/tmate-msgpack.c new file mode 100644 index 00000000..69fc5a6a --- /dev/null +++ b/tmate-msgpack.c @@ -0,0 +1,164 @@ +#include "tmate.h" + +static void on_encoder_buffer_ready(evutil_socket_t fd, short what, void *arg) +{ + struct tmate_encoder *encoder = arg; + + encoder->ev_active = false; + encoder->ready_callback(encoder->userdata, encoder->buffer); +} + +static int on_encoder_write(void *userdata, const char *buf, unsigned int len) +{ + struct tmate_encoder *encoder = userdata; + + if (evbuffer_add(encoder->buffer, buf, len) < 0) + tmate_fatal("Cannot buffer encoded data"); + + if (!encoder->ev_active) { + event_active(&encoder->ev_buffer, EV_READ, 0); + encoder->ev_active = true; + } + + return 0; +} + +void tmate_encoder_init(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata) +{ + msgpack_packer_init(&encoder->pk, encoder, &on_encoder_write); + encoder->buffer = evbuffer_new(); + encoder->ready_callback = callback; + encoder->userdata = userdata; + + if (!encoder->buffer) + tmate_fatal("Can't allocate buffer"); + + event_assign(&encoder->ev_buffer, ev_base, -1, + EV_READ | EV_PERSIST, on_encoder_buffer_ready, encoder); + + event_add(&encoder->ev_buffer, NULL); + + encoder->ev_active = false; +} + +static void decoder_error(void) +{ + tmate_print_stack_trace(); + tmate_fatal("Received a bad message"); +} + +void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj) +{ + if (obj.type != MSGPACK_OBJECT_ARRAY) + decoder_error(); + + uk->argv = obj.via.array.ptr; + uk->argc = obj.via.array.size; +} + +int64_t unpack_int(struct tmate_unpacker *uk) +{ + int64_t val; + + if (uk->argc == 0) + 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; +} + +void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len) +{ + if (uk->argc == 0) + decoder_error(); + +#if NEW_MSGPACK_API + if (uk->argv[0].type != MSGPACK_OBJECT_STR && + uk->argv[0].type != MSGPACK_OBJECT_BIN) + decoder_error(); + + *len = uk->argv[0].via.str.size; + *buf = uk->argv[0].via.str.ptr; +#else + if (uk->argv[0].type != MSGPACK_OBJECT_RAW) + decoder_error(); + + *len = uk->argv[0].via.raw.size; + *buf = uk->argv[0].via.raw.ptr; +#endif + + uk->argv++; + uk->argc--; +} + +char *unpack_string(struct tmate_unpacker *uk) +{ + const char *buf; + char *alloc_buf; + size_t len; + + unpack_buffer(uk, &buf, &len); + + alloc_buf = xmalloc(len + 1); + memcpy(alloc_buf, buf, len); + alloc_buf[len] = '\0'; + + return alloc_buf; +} + +void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested) +{ + if (uk->argc == 0) + decoder_error(); + + init_unpacker(nested, uk->argv[0]); + + uk->argv++; + uk->argc--; +} + +void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, + void *userdata) +{ + if (!msgpack_unpacker_init(&decoder->unpacker, TMATE_MAX_MESSAGE_SIZE)) + tmate_fatal("Cannot initialize the unpacker"); + decoder->reader = reader; + decoder->userdata = userdata; +} + +void tmate_decoder_get_buffer(struct tmate_decoder *decoder, + char **buf, size_t *len) +{ + ssize_t current_size = msgpack_unpacker_message_size(&decoder->unpacker); + if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker, + TMATE_MAX_MESSAGE_SIZE - current_size)) + tmate_fatal("cannot expand decoder buffer"); + + *buf = msgpack_unpacker_buffer(&decoder->unpacker); + *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); +} + +void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) +{ + struct tmate_unpacker _uk, *uk = &_uk; + msgpack_unpacked result; + + msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); + + msgpack_unpacked_init(&result); + while (msgpack_unpacker_next(&decoder->unpacker, &result)) { + init_unpacker(uk, result.data); + decoder->reader(decoder->userdata, uk); + } + msgpack_unpacked_destroy(&result); +} diff --git a/tmate-slave.c b/tmate-slave.c index 4850d25d..93f0c814 100644 --- a/tmate-slave.c +++ b/tmate-slave.c @@ -16,13 +16,10 @@ #include #include #include +#include #include "tmate.h" -struct tmate_decoder *tmate_decoder; -struct tmate_encoder *tmate_encoder; -int tmux_socket_fd; -const char *tmate_session_token; -const char *tmate_session_token_ro; +struct tmate_session _tmate_session, *tmate_session = &_tmate_session; extern FILE *log_file; @@ -33,7 +30,7 @@ static int dev_urandom_fd; extern int server_create_socket(void); extern int client_connect(char *path, int start_server); -struct tmate_settings tmate_settings = { +struct tmate_settings _tmate_settings = { .keys_dir = TMATE_SSH_DEFAULT_KEYS_DIR, .ssh_port = TMATE_SSH_DEFAULT_PORT, .master_hostname = TMATE_DEFAULT_MASTER_HOST, @@ -43,6 +40,8 @@ struct tmate_settings tmate_settings = { .use_syslog = false, }; +struct tmate_settings *tmate_settings = &_tmate_settings; + static void usage(void) { fprintf(stderr, "usage: tmate-slave [-k keys_dir] [-p port] [-m master_hostname] [-q master_port] [-s] [-v]\n"); @@ -61,6 +60,18 @@ long tmate_get_random_long(void) return val; } +extern int server_shutdown; +extern int server_fd; +extern void server_send_shutdown(void); +void request_server_termination(void) +{ + if (server_fd) { + server_shutdown = 1; + server_send_shutdown(); + } else + exit(1); +} + int main(int argc, char **argv, char **envp) { int opt; @@ -68,25 +79,25 @@ int main(int argc, char **argv, char **envp) while ((opt = getopt(argc, argv, "k:p:lvm:q:")) != -1) { switch (opt) { case 'p': - tmate_settings.ssh_port = atoi(optarg); + tmate_settings->ssh_port = atoi(optarg); break; case 'k': - tmate_settings.keys_dir = xstrdup(optarg); + tmate_settings->keys_dir = xstrdup(optarg); break; case 's': - tmate_settings.use_syslog = true; + tmate_settings->use_syslog = true; break; case 'v': - tmate_settings.log_level++; + tmate_settings->log_level++; break; case 'm': - tmate_settings.master_hostname = xstrdup(optarg); + tmate_settings->master_hostname = xstrdup(optarg); break; case 'q': - tmate_settings.master_port = atoi(optarg); + tmate_settings->master_port = atoi(optarg); break; case 'h': - tmate_settings.tmate_host = xstrdup(optarg); + tmate_settings->tmate_host = xstrdup(optarg); break; default: usage(); @@ -94,18 +105,18 @@ int main(int argc, char **argv, char **envp) } } - if (!tmate_settings.tmate_host) { + if (!tmate_settings->tmate_host) { char hostname[255]; if (gethostname(hostname, sizeof(hostname)) < 0) tmate_fatal("cannot get hostname"); - tmate_settings.tmate_host = xstrdup(hostname); + tmate_settings->tmate_host = xstrdup(hostname); } cmdline = *argv; cmdline_end = *envp; init_logging("tmate-ssh", - tmate_settings.use_syslog, tmate_settings.log_level); + tmate_settings->use_syslog, tmate_settings->log_level); tmate_preload_trace_lib(); @@ -117,7 +128,8 @@ int main(int argc, char **argv, char **envp) (mkdir(TMATE_WORKDIR "/jail", 0700) < 0 && errno != EEXIST)) tmate_fatal("Cannot prepare session in " TMATE_WORKDIR); - tmate_ssh_server_main(tmate_settings.keys_dir, tmate_settings.ssh_port); + tmate_ssh_server_main(tmate_session, + tmate_settings->keys_dir, tmate_settings->ssh_port); return 0; } @@ -139,33 +151,33 @@ static char *get_random_token(void) return token; } -static void set_session_token(struct tmate_ssh_client *client, +static void set_session_token(struct tmate_session *session, const char *token) { - tmate_session_token = xstrdup(token); + session->session_token = xstrdup(token); strcpy(socket_path, TMATE_WORKDIR "/sessions/"); strcat(socket_path, token); memset(cmdline, 0, cmdline_end - cmdline); sprintf(cmdline, "tmate-slave [%s] %s %s", - tmate_session_token, - client->role == TMATE_ROLE_SERVER ? "(server)" : "(client)", - client->ip_address); + session->session_token, + session->ssh_client.role == TMATE_ROLE_SERVER ? "(server)" : "(client)", + session->ssh_client.ip_address); } -static void create_session_ro_symlink(void) +static void create_session_ro_symlink(struct tmate_session *session) { char session_ro_path[MAXPATHLEN]; - tmate_session_token_ro = get_random_token(); + session->session_token_ro = get_random_token(); #ifdef DEVENV - strcpy((char *)tmate_session_token_ro, "READONLYTOKENFORDEVENV000"); + strcpy((char *)session->session_token_ro, "READONLYTOKENFORDEVENV000"); #endif strcpy(session_ro_path, TMATE_WORKDIR "/sessions/"); - strcat(session_ro_path, tmate_session_token_ro); + strcat(session_ro_path, session->session_token_ro); unlink(session_ro_path); - if (symlink(tmate_session_token, session_ro_path) < 0) + if (symlink(session->session_token, session_ro_path) < 0) tmate_fatal("Cannot create read-only symlink"); } @@ -250,9 +262,8 @@ static void jail(void) uid = pw->pw_uid; gid = pw->pw_gid; - /* - * We are already in a new PID namespace (from the server fork). - */ + if (getuid() != 0) + tmate_fatal("Need root priviledges"); if (chroot(TMATE_WORKDIR "/jail") < 0) tmate_fatal("Cannot chroot()"); @@ -260,6 +271,9 @@ static void jail(void) if (chdir("/") < 0) tmate_fatal("Cannot chdir()"); + if (unshare(CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWNET) < 0) + tmate_fatal("Cannot create new namespace"); + if (setgroups(1, (gid_t[]){gid}) < 0) tmate_fatal("Cannot setgroups()"); @@ -272,8 +286,8 @@ static void jail(void) if (nice(1) < 0) tmate_fatal("Cannot nice()"); - tmate_debug("Dropped priviledges to %s (%d,%d)", - TMATE_JAIL_USER, uid, gid); + tmate_debug("Dropped priviledges to %s (%d,%d), jailed in %s", + TMATE_JAIL_USER, uid, gid, TMATE_WORKDIR "/jail"); } static void setup_ncurse(int fd, const char *name) @@ -283,8 +297,10 @@ static void setup_ncurse(int fd, const char *name) tmate_fatal("Cannot setup terminal"); } -static void tmate_spawn_slave_server(struct tmate_ssh_client *client) +static void tmate_spawn_slave_server(struct tmate_session *session) { + struct tmate_ssh_client *client = &session->ssh_client; + char *token; struct tmate_encoder encoder; struct tmate_decoder decoder; @@ -294,17 +310,17 @@ static void tmate_spawn_slave_server(struct tmate_ssh_client *client) strcpy(token, "SUPERSECURETOKENFORDEVENV"); #endif - set_session_token(client, token); + set_session_token(session, token); free(token); tmate_debug("Spawning slave server for %s at %s (%s)", client->username, client->ip_address, client->pubkey); - tmux_socket_fd = server_create_socket(); - if (tmux_socket_fd < 0) + session->tmux_socket_fd = server_create_socket(); + if (session->tmux_socket_fd < 0) tmate_fatal("Cannot create to the tmux socket"); - create_session_ro_symlink(); + create_session_ro_symlink(session); /* * Needed to initialize the database used in tty-term.c. @@ -312,27 +328,23 @@ static void tmate_spawn_slave_server(struct tmate_ssh_client *client) */ setup_ncurse(STDOUT_FILENO, "screen-256color"); - close_fds_except((int[]){tmux_socket_fd, - ssh_get_fd(client->session), + close_fds_except((int[]){session->tmux_socket_fd, + ssh_get_fd(session->ssh_client.session), log_file ? fileno(log_file) : -1}, 3); jail(); - ev_base = osdep_event_init(); + event_reinit(ev_base); - tmate_encoder_init(&encoder); - tmate_decoder_init(&decoder); - tmate_encoder = &encoder; - tmate_decoder = &decoder; - - tmate_ssh_client_init(client, &encoder, &decoder); + tmate_daemon_init(session); tmux_server_init(IDENTIFY_UTF8 | IDENTIFY_256COLOURS); /* never reached */ } -static void tmate_spawn_slave_client(struct tmate_ssh_client *client) +static void tmate_spawn_slave_client(struct tmate_session *session) { + struct tmate_ssh_client *client = &session->ssh_client; char *argv_rw[] = {(char *)"attach", NULL}; char *argv_ro[] = {(char *)"attach", (char *)"-r", NULL}; char **argv = argv_rw; @@ -351,13 +363,13 @@ static void tmate_spawn_slave_client(struct tmate_ssh_client *client) tmate_fatal("Invalid token"); } - set_session_token(client, token); + set_session_token(session, token); tmate_debug("Spawning slave client for %s (%s)", client->ip_address, client->pubkey); - tmux_socket_fd = client_connect(socket_path, 0); - if (tmux_socket_fd < 0) { + session->tmux_socket_fd = client_connect(socket_path, 0); + if (session->tmux_socket_fd < 0) { random_sleep(); /* for making timing attacks harder */ ssh_echo(client, EXPIRED_TOKEN_ERROR_STR); tmate_fatal("Expired token"); @@ -370,16 +382,16 @@ static void tmate_spawn_slave_client(struct tmate_ssh_client *client) * 2) We prevent any input (aside from the window size) to go through * to the server. */ - client->readonly = 0; + session->readonly = false; if (lstat(socket_path, &fstat) < 0) tmate_fatal("Cannot fstat()"); if (S_ISLNK(fstat.st_mode)) { - client->readonly = 1; + session->readonly = true; argv = argv_ro; argc = 2; } - if (openpty(&client->pty, &slave_pty, NULL, NULL, NULL) < 0) + if (openpty(&session->pty, &slave_pty, NULL, NULL, NULL) < 0) tmate_fatal("Cannot allocate pty"); dup2(slave_pty, STDIN_FILENO); @@ -388,23 +400,24 @@ static void tmate_spawn_slave_client(struct tmate_ssh_client *client) setup_ncurse(slave_pty, "screen-256color"); close_fds_except((int[]){STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, - tmux_socket_fd, ssh_get_fd(client->session), - client->pty, log_file ? fileno(log_file) : -1}, 7); + session->tmux_socket_fd, + ssh_get_fd(session->ssh_client.session), + session->pty, log_file ? fileno(log_file) : -1}, 7); jail(); + event_reinit(ev_base); - ev_base = osdep_event_init(); - - tmate_ssh_client_pty_init(client); + tmate_ssh_client_pty_init(session); ret = client_main(argc, argv, IDENTIFY_UTF8 | IDENTIFY_256COLOURS); - tmate_flush_pty(client); + tmate_flush_pty(session); exit(ret); } -void tmate_spawn_slave(struct tmate_ssh_client *client) +void tmate_spawn_slave(struct tmate_session *session) { - if (client->role == TMATE_ROLE_SERVER) - tmate_spawn_slave_server(client); + + if (session->ssh_client.role == TMATE_ROLE_SERVER) + tmate_spawn_slave_server(session); else - tmate_spawn_slave_client(client); + tmate_spawn_slave_client(session); } diff --git a/tmate-ssh-client-pty.c b/tmate-ssh-client-pty.c index dde02ae9..79f0230c 100644 --- a/tmate-ssh-client-pty.c +++ b/tmate-ssh-client-pty.c @@ -1,62 +1,40 @@ -#include #include -#include #include #include "tmate.h" extern void client_write_server(enum msgtype type, void *buf, size_t len); -static void consume_channel(struct tmate_ssh_client *client) +static int on_ssh_channel_read(ssh_session _session, ssh_channel channel, + void *_data, uint32_t total_len, + int is_stderr, void *userdata) { - ssize_t len, written; - char buf[4096]; - char *ptr; + struct tmate_session *session = userdata; + char *data = _data; + size_t written = 0; + ssize_t len; - for (;;) { - len = ssh_channel_read_nonblocking(client->channel, - buf, sizeof(buf), 0); - if (len < 0) { - if (!ssh_is_connected(client->session)) - tmate_fatal("Disconnected"); + if (session->readonly) + return total_len; - tmate_fatal("Error reading from channel: %s", - ssh_get_error(client->session)); - } + setblocking(session->pty, 1); + while (total_len) { + len = write(session->pty, data, total_len); + if (len < 0) + tmate_fatal("Error writing to pty"); - if (len == 0) - return; - - if (client->readonly) - continue; - - 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); + total_len -= len; + written += len; + data += len; } + setblocking(session->pty, 0); + + return written; } -static void on_session_event(struct tmate_ssh_client *client) +static int on_ssh_message_callback(ssh_session _session, ssh_message msg, void *arg) { - ssh_execute_message_callbacks(client->session); - consume_channel(client); -} + struct tmate_session *session = arg; -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 *client, - ssh_message msg) -{ if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL && ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_WINDOW_CHANGE) { struct winsize ws; @@ -64,7 +42,7 @@ static int message_callback(struct tmate_ssh_client *client, ws.ws_col = ssh_message_channel_request_pty_width(msg); ws.ws_row = ssh_message_channel_request_pty_height(msg); - ioctl(client->pty, TIOCSWINSZ, &ws); + ioctl(session->pty, TIOCSWINSZ, &ws); client_write_server(MSG_RESIZE, NULL, 0); return 1; @@ -72,27 +50,13 @@ static int message_callback(struct tmate_ssh_client *client, 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 *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 *client) +static void on_pty_event(struct tmate_session *session) { ssize_t len, written; char buf[4096]; for (;;) { - len = read(client->pty, buf, sizeof(buf)); + len = read(session->pty, buf, sizeof(buf)); if (len < 0) { if (errno == EAGAIN) return; @@ -102,10 +66,10 @@ static void on_pty_event(struct tmate_ssh_client *client) if (len == 0) tmate_fatal("pty reached EOF"); - written = ssh_channel_write(client->channel, buf, len); + written = ssh_channel_write(session->ssh_client.channel, buf, len); if (written < 0) tmate_fatal("Error writing to channel: %s", - ssh_get_error(client->session)); + ssh_get_error(session->ssh_client.session)); if (len != written) tmate_fatal("Cannot write %d bytes, wrote %d", (int)len, (int)written); @@ -117,25 +81,29 @@ 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 *client) +void tmate_flush_pty(struct tmate_session *session) { - on_pty_event(client); - close(client->pty); + on_pty_event(session); + close(session->pty); } -static void register_pty_event(struct tmate_ssh_client *client) +void tmate_ssh_client_pty_init(struct tmate_session *session) { - 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); -} + struct tmate_ssh_client *client = &session->ssh_client; -void tmate_ssh_client_pty_init(struct tmate_ssh_client *client) -{ - ioctl(client->pty, TIOCSWINSZ, &client->winsize_pty); - register_session_fd_event(client); - register_pty_event(client); + ioctl(session->pty, TIOCSWINSZ, &session->ssh_client.winsize_pty); - tmate_start_keepalive_timer(client); + memset(&client->channel_cb, 0, sizeof(client->channel_cb)); + ssh_callbacks_init(&client->channel_cb); + client->channel_cb.userdata = session; + client->channel_cb.channel_data_function = on_ssh_channel_read, + ssh_set_channel_callbacks(client->channel, &client->channel_cb); + + ssh_set_message_callback(session->ssh_client.session, + on_ssh_message_callback, session); + + setblocking(session->pty, 0); + event_assign(&session->ev_pty, ev_base, session->pty, + EV_READ | EV_PERSIST, __on_pty_event, session); + event_add(&session->ev_pty, NULL); } diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index e27fe130..256ce96a 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -1,116 +1,77 @@ #include "tmate.h" -#include -#include -#include -extern int server_shutdown; -extern void server_send_shutdown(void); - -#define request_termination(str, ...) do { \ - tmate_info(str, ## __VA_ARGS__); \ - server_shutdown = 1; \ - server_send_shutdown(); \ -} while(0) - -static void consume_channel(struct tmate_ssh_client *client) +static void on_decoder_read(void *userdata, struct tmate_unpacker *uk) { + struct tmate_session *session = userdata; + tmate_dispatch_daemon_message(session, uk); +} + +static int on_ssh_channel_read(ssh_session _session, ssh_channel channel, + void *_data, uint32_t total_len, + int is_stderr, void *userdata) +{ + struct tmate_session *session = userdata; + char *data = _data; + size_t written = 0; char *buf; - ssize_t len; + size_t len; - for (;;) { - tmate_decoder_get_buffer(client->decoder, &buf, &len); - if (len == 0) { - request_termination("Decoder buffer full"); - break; - } + while (total_len) { + tmate_decoder_get_buffer(&session->client_decoder, &buf, &len); - len = ssh_channel_read_nonblocking(client->channel, - buf, len, 0); - if (len < 0) { - if (!ssh_is_connected(client->session)) - request_termination("Disconnected"); - else - request_termination("Error reading from channel: %s", - ssh_get_error(client->session)); - break; - } if (len == 0) - break; + tmate_fatal("No more room in client decoder. Message too big?"); - tmate_decoder_commit(client->decoder, len); + if (len > total_len) + len = total_len; + + memcpy(buf, data, len); + + tmate_decoder_commit(&session->client_decoder, len); + + total_len -= len; + written += len; + data += len; } + + return written; } -static void on_session_event(struct tmate_ssh_client *client) +static void on_encoder_write(void *userdata, struct evbuffer *buffer) { - ssh_execute_message_callbacks(client->session); - consume_channel(client); -} - -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; + struct tmate_session *session = userdata; ssize_t len, written; char *buf; - if (server_shutdown) - return; - - for (;;) { - len = evbuffer_get_length(evb); + for(;;) { + len = evbuffer_get_length(buffer); if (!len) break; - buf = evbuffer_pullup(evb, -1); + buf = evbuffer_pullup(buffer, -1); - written = ssh_channel_write(client->channel, buf, len); + written = ssh_channel_write(session->ssh_client.channel, buf, len); if (written < 0) { - request_termination("Error writing to channel: %s", - ssh_get_error(client->session)); + tmate_warn("Error writing to channel: %s", + ssh_get_error(session->ssh_client.session)); + request_server_termination(); break; } - evbuffer_drain(evb, written); + evbuffer_drain(buffer, written); } } -static void __flush_input_stream(evutil_socket_t fd, short what, void *arg) +void tmate_daemon_init(struct tmate_session *session) { - 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); - - tmate_start_keepalive_timer(client); + struct tmate_ssh_client *client = &session->ssh_client; + + memset(&client->channel_cb, 0, sizeof(client->channel_cb)); + ssh_callbacks_init(&client->channel_cb); + client->channel_cb.userdata = session; + client->channel_cb.channel_data_function = on_ssh_channel_read, + ssh_set_channel_callbacks(client->channel, &client->channel_cb); + + tmate_encoder_init(&session->client_encoder, on_encoder_write, session); + tmate_decoder_init(&session->client_decoder, on_decoder_read, session); } diff --git a/tmate-ssh-server.c b/tmate-ssh-server.c index 856af55f..24229141 100644 --- a/tmate-ssh-server.c +++ b/tmate-ssh-server.c @@ -4,28 +4,24 @@ #include #include #include -#include -#include #include #include #include #include "tmate.h" -#define SSH_GRACE_PERIOD 60 - +static void start_keepalive_timer(struct tmate_ssh_client *client); static void on_keepalive_timer(evutil_socket_t fd, short what, void *arg) { struct tmate_ssh_client *client = arg; + ssh_send_keepalive(client->session); - tmate_start_keepalive_timer(client); + start_keepalive_timer(client); } -void tmate_start_keepalive_timer(struct tmate_ssh_client *client) +static void start_keepalive_timer(struct tmate_ssh_client *client) { - struct timeval tv; - tv.tv_sec = TMATE_KEEPALIVE; - tv.tv_usec = 0; + struct timeval tv = { TMATE_SSH_KEEPALIVE, 0 }; evtimer_assign(&client->ev_keepalive_timer, ev_base, on_keepalive_timer, client); @@ -65,12 +61,6 @@ static int subsystem_request(ssh_session session, ssh_channel channel, return 0; } -static struct ssh_channel_callbacks_struct ssh_channel_cb = { - .channel_pty_request_function = pty_request, - .channel_shell_request_function = shell_request, - .channel_subsystem_request_function = subsystem_request, -}; - static ssh_channel channel_open_request_cb(ssh_session session, void *userdata) { struct tmate_ssh_client *client = userdata; @@ -89,9 +79,13 @@ static ssh_channel channel_open_request_cb(ssh_session session, void *userdata) if (!client->channel) tmate_fatal("Error getting channel"); - ssh_channel_cb.userdata = client; - ssh_callbacks_init(&ssh_channel_cb); - ssh_set_channel_callbacks(client->channel, &ssh_channel_cb); + memset(&client->channel_cb, 0, sizeof(client->channel_cb)); + ssh_callbacks_init(&client->channel_cb); + client->channel_cb.userdata = client; + client->channel_cb.channel_pty_request_function = pty_request, + client->channel_cb.channel_shell_request_function = shell_request, + client->channel_cb.channel_subsystem_request_function = subsystem_request, + ssh_set_channel_callbacks(client->channel, &client->channel_cb); return client->channel; } @@ -121,16 +115,39 @@ static struct ssh_server_callbacks_struct ssh_server_cb = { .channel_open_request_session_function = channel_open_request_cb, }; -static void client_bootstrap(struct tmate_ssh_client *client) +static void on_ssh_read(evutil_socket_t fd, short what, void *arg) { + struct tmate_ssh_client *client = arg; + ssh_execute_message_callbacks(client->session); + + if (!ssh_is_connected(client->session)) { + tmate_warn("SSH Disconnected"); + + /* For graceful tmux client termination */ + request_server_termination(); + } +} + +static void register_on_ssh_read(struct tmate_ssh_client *client) +{ + event_assign(&client->ev_ssh, ev_base, ssh_get_fd(client->session), + EV_READ | EV_PERSIST, on_ssh_read, client); + event_add(&client->ev_ssh, NULL); +} + +static void client_bootstrap(struct tmate_session *_session) +{ + struct tmate_ssh_client *client = &_session->ssh_client; int auth = 0; - int grace_period = SSH_GRACE_PERIOD; + int grace_period = TMATE_SSH_GRACE_PERIOD; ssh_event mainloop; ssh_session session = client->session; ssh_message msg; tmate_notice("Bootstrapping ssh client ip=%s", client->ip_address); + ev_base = osdep_event_init(); + /* new process group, we don't want to die with our parent (upstart) */ setpgid(0, 0); @@ -163,7 +180,11 @@ static void client_bootstrap(struct tmate_ssh_client *client) } alarm(0); - tmate_spawn_slave(client); + + start_keepalive_timer(client); + register_on_ssh_read(client); + + tmate_spawn_slave(_session); /* never reached */ } @@ -182,13 +203,13 @@ static void handle_sigchld(void) static void handle_sigalrm(void) { - tmate_fatal("Connection grace period (%d) passed", SSH_GRACE_PERIOD); + tmate_fatal("Connection grace period (%d) passed", TMATE_SSH_GRACE_PERIOD); } static void handle_sigsegv(void) { tmate_info("CRASH, printing stack trace"); - tmate_print_trace(); + tmate_print_stack_trace(); tmate_fatal("CRASHED"); } @@ -208,14 +229,6 @@ static void setup_signals(void) signal(SIGSEGV, signal_handler); } -static pid_t namespace_fork(void) -{ - /* XXX we are breaking getpid() libc cache. Bad libc. */ - unsigned long flags; - flags = CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWNET; - return syscall(SYS_clone, flags | SIGCHLD, NULL, NULL, NULL); -} - static int get_ip(int fd, char *dst, size_t len) { struct sockaddr sa; @@ -243,8 +256,6 @@ static int get_ip(int fd, char *dst, size_t len) return 0; } -struct tmate_ssh_client tmate_client; - static void ssh_log_function(int priority, const char *function, const char *buffer, void *userdata) { @@ -281,9 +292,10 @@ static ssh_bind prepare_ssh(const char *keys_dir, int port) return bind; } -void tmate_ssh_server_main(const char *keys_dir, int port) +void tmate_ssh_server_main(struct tmate_session *session, + const char *keys_dir, int port) { - struct tmate_ssh_client *client = &tmate_client; + struct tmate_ssh_client *client = &session->ssh_client; ssh_bind bind; pid_t pid; @@ -293,6 +305,9 @@ void tmate_ssh_server_main(const char *keys_dir, int port) for (;;) { client->session = ssh_new(); client->channel = NULL; + client->winsize_pty.ws_col = 80; + client->winsize_pty.ws_row = 24; + if (!client->session) tmate_fatal("Cannot initialize session"); @@ -303,12 +318,8 @@ void tmate_ssh_server_main(const char *keys_dir, int port) client->ip_address, sizeof(client->ip_address)) < 0) tmate_fatal("Error getting IP address from connection"); - if ((pid = namespace_fork()) < 0) { - if (getuid() == 0) - tmate_fatal("Can't fork in new namespace, are you running a recent kernel?"); - else - tmate_fatal("Can't fork in new namespace, run me with root priviledges"); - } + if ((pid = fork()) < 0) + tmate_fatal("Can't fork"); if (pid) { tmate_info("Child spawned pid=%d, ip=%s", @@ -316,8 +327,8 @@ void tmate_ssh_server_main(const char *keys_dir, int port) ssh_free(client->session); } else { ssh_bind_free(bind); - tmate_session_token = "init"; - client_bootstrap(client); + session->session_token = "init"; + client_bootstrap(session); } } } diff --git a/tmate.h b/tmate.h index 71fd9562..23f7c25d 100644 --- a/tmate.h +++ b/tmate.h @@ -5,9 +5,13 @@ #include #include #include +#include #include #include "tmux.h" +struct tmate_session; + +/* log.c */ extern void init_logging(const char *program_name, bool use_syslog, int log_level); extern void printflike2 tmate_log(int level, const char *msg, ...); @@ -22,7 +26,83 @@ extern void printflike2 tmate_log(int level, const char *msg, ...); exit(1); \ }) -/* tmate-encoder.c */ +/* tmate-msgpack.c */ + +typedef void tmate_encoder_write_cb(void *userdata, struct evbuffer *buffer); + +struct tmate_encoder { + msgpack_packer pk; + tmate_encoder_write_cb *ready_callback; + void *userdata; + struct evbuffer *buffer; + struct event ev_buffer; + bool ev_active; +}; + +extern void tmate_encoder_init(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata); + +#define NEW_MSGPACK_API 0 + +#if NEW_MSG_PACK +#define msgpack_pack_buffer(pk, buf, len) ({ \ + msgpack_pack_bin(pk, len); \ + msgpack_pack_bin_body(pk, buf, len); \ +}) + +#define msgpack_pack_string(pk, str) ({ \ + int __strlen = strlen(str); \ + msgpack_pack_str(pk, __strlen); \ + msgpack_pack_str_body(pk, str, __strlen); \ +}) +#else +/* old msgpack version */ +#define msgpack_pack_buffer(pk, buf, len) ({ \ + msgpack_pack_raw(pk, len); \ + msgpack_pack_raw_body(pk, buf, len); \ +}) + +#define msgpack_pack_string(pk, str) ({ \ + int __strlen = strlen(str); \ + msgpack_pack_raw(pk, __strlen); \ + msgpack_pack_raw_body(pk, str, __strlen); \ +}) +#endif + +#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, __VA_ARGS__) + +struct tmate_unpacker; +struct tmate_decoder; +typedef void tmate_decoder_reader(void *userdata, struct tmate_unpacker *uk); + +struct tmate_decoder { + struct msgpack_unpacker unpacker; + tmate_decoder_reader *reader; + void *userdata; +}; + +extern void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata); +extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len); +extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); + +struct tmate_unpacker { + msgpack_object *argv; + int argc; +}; + +extern void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj); +extern int64_t unpack_int(struct tmate_unpacker *uk); +extern void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len); +extern char *unpack_string(struct tmate_unpacker *uk); +extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested); + +#define unpack_each(nested_uk, tmp_uk, uk) \ + for (unpack_array(uk, tmp_uk); \ + (tmp_uk)->argc > 0 && (init_unpacker(nested_uk, (tmp_uk)->argv[0]), 1); \ + (tmp_uk)->argv++, (tmp_uk)->argc--) + +/* tmate-client-encoder.c */ #define TMATE_LATEST_VERSION "1.8.10" @@ -35,14 +115,7 @@ enum tmate_client_commands { TMATE_CLIENT_READY, }; -struct tmate_encoder { - struct evbuffer *buffer; - struct event ev_readable; - struct event ev_notify_timer; - msgpack_packer pk; -}; - -extern void tmate_encoder_init(struct tmate_encoder *encoder); +extern void tmate_client_encoder_init(struct tmate_encoder *encoder); extern void printflike1 tmate_notify(const char *fmt, ...); extern void printflike2 tmate_notify_later(int timeout, const char *fmt, ...); @@ -57,10 +130,11 @@ extern int tmate_should_exec_cmd_locally(const struct cmd_entry *cmd); extern void tmate_send_env(const char *name, const char *value); extern void tmate_send_client_ready(void); -/* tmate-decoder.c */ +/* tmate-client-decoder.c */ #define TMATE_HLIMIT 2000 -#define TMATE_MAX_MESSAGE_SIZE (16*1024) + /* 17 and not 16 because the sender does not takes into account envelope size */ +#define TMATE_MAX_MESSAGE_SIZE (17*1024) extern char *tmate_left_status, *tmate_right_status; @@ -77,19 +151,23 @@ enum tmate_commands { #define TMATE_PANE_ACTIVE 1 -struct tmate_decoder { - int protocol; - - 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); +extern void tmate_dispatch_daemon_message(struct tmate_session *session, + struct tmate_unpacker *uk); /* tmate-ssh-client.c */ +extern void tmate_daemon_init(struct tmate_session *session); + +/* tmate-ssh-client-pty.c */ + +extern void tmate_ssh_client_pty_init(struct tmate_session *session); +extern void tmate_flush_pty(struct tmate_session *session); + +/* tmate-ssh-server.c */ + +#define TMATE_SSH_BANNER "tmate" +#define TMATE_SSH_KEEPALIVE 60 + #define TMATE_ROLE_SERVER 1 #define TMATE_ROLE_CLIENT 2 @@ -98,12 +176,14 @@ struct tmate_ssh_client { ssh_session session; ssh_channel channel; + /* + * We need to store the entire callback struct because + * libssh stores the userdata within the cb struct... + */ + struct ssh_channel_callbacks_struct channel_cb; int role; - struct tmate_encoder *encoder; - struct tmate_decoder *decoder; - char *username; char *pubkey; @@ -111,53 +191,21 @@ struct tmate_ssh_client { struct event ev_ssh; struct event ev_keepalive_timer; - - /* only for client-pty */ - int pty; - struct event ev_pty; - int readonly; }; -extern void tmate_ssh_client_init(struct tmate_ssh_client *client, - struct tmate_encoder *encoder, - struct tmate_decoder *decoder); -/* tmate-ssh-client-pty.c */ - -extern void tmate_ssh_client_pty_init(struct tmate_ssh_client *client); -extern void tmate_flush_pty(struct tmate_ssh_client *client); - -/* tmate-ssh-server.c */ - -#define TMATE_SSH_BANNER "tmate" -#define TMATE_KEEPALIVE 60 - -#ifdef TMATE_RECORD_REPLAY -extern int tmate_session_log_fd; -#endif - -extern struct tmate_ssh_client tmate_client; -extern void tmate_start_keepalive_timer(struct tmate_ssh_client *client); -extern void tmate_ssh_server_main(const char *keys_dir, int port); +extern void tmate_ssh_server_main(struct tmate_session *session, + const char *keys_dir, int port); /* tmate-slave.c */ -struct tmate_settings { - const char *keys_dir; - int ssh_port; - const char *master_hostname; - int master_port; - const char *tmate_host; - int log_level; - bool use_syslog; -}; -extern struct tmate_settings tmate_settings; - #ifdef DEVENV #define TMATE_SSH_DEFAULT_PORT 2200 #else #define TMATE_SSH_DEFAULT_PORT 22 #endif +#define TMATE_SSH_GRACE_PERIOD 60 + #define TMATE_SSH_DEFAULT_KEYS_DIR "keys" #define TMATE_DEFAULT_MASTER_HOST NULL @@ -167,24 +215,51 @@ extern struct tmate_settings tmate_settings; #define TMATE_WORKDIR "/tmp/tmate" #define TMATE_JAIL_USER "nobody" -extern int tmate_port; -extern struct tmate_encoder *tmate_encoder; -extern struct tmate_decoder *tmate_decoder; -extern int tmux_socket_fd; -extern char *tmate_host; -extern const char *tmate_session_token; -extern const char *tmate_session_token_ro; +struct tmate_settings { + const char *keys_dir; + int ssh_port; + const char *master_hostname; + int master_port; + const char *tmate_host; + int log_level; + bool use_syslog; +}; +extern struct tmate_settings *tmate_settings; +struct tmate_session { + struct tmate_ssh_client ssh_client; + int tmux_socket_fd; + + /* only for deamon */ + + struct tmate_encoder client_encoder; + struct tmate_decoder client_decoder; + int client_protocol_version; + + struct tmate_encoder master_encoder; + struct tmate_decoder master_decoder; + + const char *session_token; + const char *session_token_ro; + + struct event ev_notify_timer; + + /* only for client-pty */ + int pty; + struct event ev_pty; + bool readonly; +}; + +extern struct tmate_session *tmate_session; extern void tmate_get_random_bytes(void *buffer, ssize_t len); extern long tmate_get_random_long(void); - -extern void tmate_reopen_logfile(void); -extern void tmate_spawn_slave(struct tmate_ssh_client *client); +extern void request_server_termination(void); +extern void tmate_spawn_slave(struct tmate_session *session); /* tmate-debug.c */ extern void tmate_preload_trace_lib(void); -extern void tmate_print_trace(void); +extern void tmate_print_stack_trace(void); /* tmux-bare.c */