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

Quick and dirty Record/Replay feature

This commit is contained in:
Nicolas Viennot 2013-07-10 22:48:24 -04:00
parent bddab8eff2
commit 23b03685a0
6 changed files with 180 additions and 6 deletions

View File

@ -181,6 +181,7 @@ dist_tmate_slave_SOURCES = \
tmate-debug.c \ tmate-debug.c \
tmate-decoder.c \ tmate-decoder.c \
tmate-encoder.c \ tmate-encoder.c \
tmate-replayer.c \
tmate-slave.c \ tmate-slave.c \
tmate-ssh-client-pty.c \ tmate-ssh-client-pty.c \
tmate-ssh-client.c \ tmate-ssh-client.c \

View File

@ -26,7 +26,11 @@ void tmate_encoder_init(struct tmate_encoder *encoder)
msgpack_pack_raw_body(pk, str, __strlen); \ msgpack_pack_raw_body(pk, str, __strlen); \
} while(0) } while(0)
#define pack(what, ...) msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__) /* 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)
void printflike1 tmate_notify(const char *fmt, ...) void printflike1 tmate_notify(const char *fmt, ...)
{ {

70
tmate-replayer.c Normal file
View File

@ -0,0 +1,70 @@
#include <ctype.h>
#include <unistd.h>
#include "tmate.h"
#ifdef TMATE_RECORD_REPLAY
#define READ_MAX_SIZE 1024
#define TIMER_INERVAL_USEC 1000
extern int server_shutdown;
extern void server_send_shutdown(void);
static void start_timer(struct tmate_replayer *replayer);
static void on_read_timer(evutil_socket_t fd, short what, void *arg)
{
struct tmate_replayer *replayer = arg;
char *buf;
ssize_t len;
if (replayer->log_fd < 0) {
evtimer_del(&replayer->ev_read_timer);
server_shutdown = 1;
server_send_shutdown();
return;
}
tmate_decoder_get_buffer(replayer->decoder, &buf, &len);
if (len == 0)
tmate_fatal("Decoder buffer full");
if (len > READ_MAX_SIZE)
len = READ_MAX_SIZE;
len = read(replayer->log_fd, buf, len);
if (len < 0)
tmate_fatal("cannot read from replay log file");
if (len == 0) {
tmate_info("Replay file reached EOF");
replayer->log_fd = -1;
} else {
tmate_decoder_commit(replayer->decoder, len);
}
start_timer(replayer);
}
static void start_timer(struct tmate_replayer *replayer)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = TIMER_INERVAL_USEC;
evtimer_assign(&replayer->ev_read_timer, ev_base,
on_read_timer, replayer);
evtimer_add(&replayer->ev_read_timer, &tv);
}
void tmate_replayer_init(struct tmate_replayer *replayer,
struct tmate_decoder *decoder,
int log_fd)
{
replayer->decoder = decoder;
replayer->log_fd = log_fd;
start_timer(replayer);
}
#endif

View File

@ -22,6 +22,11 @@ int tmux_socket_fd;
const char *tmate_session_token = "main"; const char *tmate_session_token = "main";
const char *tmate_session_token_ro = "ro-main"; const char *tmate_session_token_ro = "ro-main";
#ifdef TMATE_RECORD_REPLAY
int tmate_session_log_fd;
static void tmate_replay_slave_server(const char *replay_file);
#endif
static char *log_path; /* NULL means stderr */ static char *log_path; /* NULL means stderr */
static char *cmdline; static char *cmdline;
static char *cmdline_end; static char *cmdline_end;
@ -33,7 +38,7 @@ extern int client_connect(char *path, int start_server);
static void usage(void) static void usage(void)
{ {
fprintf(stderr, "usage: tmate-slave [-k keys_dir] [-l logfile] [-p PORT] [-v]\n"); fprintf(stderr, "usage: tmate-slave [-k keys_dir] [-l logfile] [-p PORT] [-r logfile] [-v]\n");
} }
void tmate_reopen_logfile(void) void tmate_reopen_logfile(void)
@ -59,8 +64,11 @@ int main(int argc, char **argv, char **envp)
int opt; int opt;
int port = TMATE_DEFAULT_PORT; int port = TMATE_DEFAULT_PORT;
const char *keys_dir = "keys"; const char *keys_dir = "keys";
#ifdef TMATE_RECORD_REPLAY
const char *replay_file = NULL;
#endif
while ((opt = getopt(argc, argv, "p:l:vk:")) != -1) { while ((opt = getopt(argc, argv, "p:l:vk:r:")) != -1) {
switch (opt) { switch (opt) {
case 'p': case 'p':
port = atoi(optarg); port = atoi(optarg);
@ -74,6 +82,13 @@ int main(int argc, char **argv, char **envp)
case 'v': case 'v':
debug_level++; debug_level++;
break; break;
case 'r':
#ifdef TMATE_RECORD_REPLAY
replay_file = optarg;
#else
fprintf(stderr, "Record/Replay not enabled\n");
#endif
break;
default: default:
usage(); usage();
return 1; return 1;
@ -85,16 +100,23 @@ int main(int argc, char **argv, char **envp)
tmate_reopen_logfile(); tmate_reopen_logfile();
tmate_preload_trace_lib();
if ((dev_urandom_fd = open("/dev/urandom", O_RDONLY)) < 0) if ((dev_urandom_fd = open("/dev/urandom", O_RDONLY)) < 0)
tmate_fatal("Cannot open /dev/urandom"); tmate_fatal("Cannot open /dev/urandom");
#ifdef TMATE_RECORD_REPLAY
if (replay_file) {
tmate_replay_slave_server(replay_file);
return 0;
}
#endif
if ((mkdir(TMATE_WORKDIR, 0700) < 0 && errno != EEXIST) || if ((mkdir(TMATE_WORKDIR, 0700) < 0 && errno != EEXIST) ||
(mkdir(TMATE_WORKDIR "/sessions", 0700) < 0 && errno != EEXIST) || (mkdir(TMATE_WORKDIR "/sessions", 0700) < 0 && errno != EEXIST) ||
(mkdir(TMATE_WORKDIR "/jail", 0700) < 0 && errno != EEXIST)) (mkdir(TMATE_WORKDIR "/jail", 0700) < 0 && errno != EEXIST))
tmate_fatal("Cannot prepare session in " TMATE_WORKDIR); tmate_fatal("Cannot prepare session in " TMATE_WORKDIR);
tmate_preload_trace_lib();
tmate_ssh_server_main(keys_dir, port); tmate_ssh_server_main(keys_dir, port);
return 0; return 0;
} }
@ -257,10 +279,36 @@ static void jail(void)
static void setup_ncurse(int fd, const char *name) static void setup_ncurse(int fd, const char *name)
{ {
int error; int error;
if (setupterm(name, fd, &error) != OK) if (setupterm((char *)name, fd, &error) != OK)
tmate_fatal("Cannot setup terminal"); tmate_fatal("Cannot setup terminal");
} }
#ifdef TMATE_RECORD_REPLAY
static void tmate_replay_slave_server(const char *replay_file)
{
struct tmate_decoder decoder;
struct tmate_replayer replayer;
tmate_debug("Replaying slave server with %s", replay_file);
tmux_socket_fd = server_create_socket();
if (tmux_socket_fd < 0)
tmate_fatal("Cannot create to the tmux socket");
tmate_session_log_fd = open(replay_file, O_RDONLY);
if (tmate_session_log_fd < 0)
tmate_fatal("cannot open session-dump.log");
ev_base = osdep_event_init();
tmate_decoder_init(&decoder);
tmate_replayer_init(&replayer, &decoder, tmate_session_log_fd);
tmux_server_init(IDENTIFY_UTF8 | IDENTIFY_256COLOURS);
/* never reached */
}
#endif
static void tmate_spawn_slave_server(struct tmate_ssh_client *client) static void tmate_spawn_slave_server(struct tmate_ssh_client *client)
{ {
char *token; char *token;
@ -290,6 +338,14 @@ static void tmate_spawn_slave_server(struct tmate_ssh_client *client)
setup_ncurse(STDOUT_FILENO, "screen-256color"); setup_ncurse(STDOUT_FILENO, "screen-256color");
close_fds_except((int[]){tmux_socket_fd, ssh_get_fd(client->session), close_fds_except((int[]){tmux_socket_fd, ssh_get_fd(client->session),
fileno(log_file)}, 7); fileno(log_file)}, 7);
#ifdef TMATE_RECORD_REPLAY
tmate_session_log_fd = open("session-log.log",
O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (tmate_session_log_fd < 0)
tmate_fatal("cannot open session-dump.log");
#endif
jail(); jail();
ev_base = osdep_event_init(); ev_base = osdep_event_init();

View File

@ -12,6 +12,25 @@ extern void server_send_shutdown(void);
server_send_shutdown(); \ server_send_shutdown(); \
} while(0) } while(0)
#ifdef TMATE_RECORD_REPLAY
static void record_session_data(const char *buf, size_t len)
{
ssize_t ret;
while (len > 0) {
ret = write(tmate_session_log_fd, buf, len);
if (ret < 0)
tmate_fatal("cannot save recording of the session");
buf += ret;
len -= ret;
}
}
#else
static inline void record_session_data(const char *buf, size_t len)
{}
#endif
static void consume_channel(struct tmate_ssh_client *client) static void consume_channel(struct tmate_ssh_client *client)
{ {
char *buf; char *buf;
@ -37,6 +56,8 @@ static void consume_channel(struct tmate_ssh_client *client)
if (len == 0) if (len == 0)
break; break;
record_session_data(buf, len);
tmate_decoder_commit(client->decoder, len); tmate_decoder_commit(client->decoder, len);
} }
} }

22
tmate.h
View File

@ -7,6 +7,8 @@
#include "tmux.h" #include "tmux.h"
#define TMATE_RECORD_REPLAY /* useful to debug crashes */
#define tmate_debug(str, ...) log_debug("[tmate] " str, ##__VA_ARGS__) #define tmate_debug(str, ...) log_debug("[tmate] " str, ##__VA_ARGS__)
#define tmate_info(str, ...) log_info("[tmate] " str, ##__VA_ARGS__) #define tmate_info(str, ...) log_info("[tmate] " str, ##__VA_ARGS__)
#define tmate_fatal(str, ...) \ #define tmate_fatal(str, ...) \
@ -73,6 +75,22 @@ extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder,
char **buf, size_t *len); char **buf, size_t *len);
extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len);
#ifdef TMATE_RECORD_REPLAY
/* tmate-replayer.c */
struct tmate_replayer {
struct tmate_decoder *decoder;
int log_fd;
struct event ev_read_timer;
};
extern void tmate_replayer_init(struct tmate_replayer *replayer,
struct tmate_decoder *decoder,
int log_fd);
#endif
/* tmate-ssh-client.c */ /* tmate-ssh-client.c */
typedef struct ssh_session_struct* ssh_session; typedef struct ssh_session_struct* ssh_session;
@ -122,6 +140,10 @@ extern void tmate_flush_pty(struct tmate_ssh_client *client);
#define TMATE_SSH_BANNER "tmate" #define TMATE_SSH_BANNER "tmate"
#define TMATE_KEEPALIVE 60 #define TMATE_KEEPALIVE 60
#ifdef TMATE_RECORD_REPLAY
extern int tmate_session_log_fd;
#endif
extern struct tmate_ssh_client tmate_client; extern struct tmate_ssh_client tmate_client;
extern void tmate_start_keepalive_timer(struct tmate_ssh_client *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(const char *keys_dir, int port);