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:
parent
bddab8eff2
commit
23b03685a0
@ -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 \
|
||||||
|
@ -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
70
tmate-replayer.c
Normal 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
|
@ -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();
|
||||||
|
@ -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
22
tmate.h
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user