static void handle_sigchld(__unused int sig) { int status; pid_t pid; while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) tmate_info("Child %d exited (%d)", pid, WEXITSTATUS(status)); if (WIFSIGNALED(status)) tmate_info("Child %d killed (%d)", pid, WTERMSIG(status)); if (WIFSTOPPED(status)) tmate_info("Child %d stopped (%d)", pid, WSTOPSIG(status)); } }
static void handle_sigsegv(__unused int sig) { /* TODO send stack trace to server */ tmate_info("CRASH, printing stack trace"); tmate_print_stack_trace(); tmate_fatal("CRASHED"); }
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_info("============ %zd stack frames ============", size); for (i = 1; i < size; i++) { #if DEBUG if (print_resolved_stack_frame(strings[i]) < 0) #endif tmate_info("%s", strings[i]); } free(strings); }
static void handle_sigchld(__unused int sig) { int status; pid_t pid; while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) { /* * It's not safe to call indirectly malloc() here, because * of potential deadlocks with ssh_bind_accept() which also * calls malloc(). (And we can't even block singals because * the accept() call is blocking. */ #if 0 if (WIFEXITED(status)) tmate_info("Child %d exited (%d)", pid, WEXITSTATUS(status)); if (WIFSIGNALED(status)) tmate_info("Child %d killed (%d)", pid, WTERMSIG(status)); if (WIFSTOPPED(status)) tmate_info("Child %d stopped (%d)", pid, WSTOPSIG(status)); #endif } }
void tmate_ssh_server_main(struct tmate_session *session, const char *keys_dir, int port) { sigset_t sigchld_set; struct tmate_ssh_client *client = &session->ssh_client; ssh_bind bind; pid_t pid; signal(SIGSEGV, handle_sigsegv); signal(SIGCHLD, handle_sigchld); sigemptyset(&sigchld_set); sigaddset(&sigchld_set, SIGCHLD); sigprocmask(SIG_BLOCK, &sigchld_set, NULL); bind = prepare_ssh(keys_dir, 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"); sigprocmask(SIG_UNBLOCK, &sigchld_set, NULL); if (ssh_bind_accept(bind, client->session) < 0) tmate_fatal("Error accepting connection: %s", ssh_get_error(bind)); sigprocmask(SIG_BLOCK, &sigchld_set, NULL); if (get_ip(ssh_get_fd(client->session), client->ip_address, sizeof(client->ip_address)) < 0) tmate_fatal("Error getting IP address from connection"); if ((pid = fork()) < 0) tmate_fatal("Can't fork"); if (pid) { tmate_info("Child spawned pid=%d, ip=%s", pid, client->ip_address); ssh_free(client->session); } else { ssh_bind_free(bind); session->session_token = "init"; client_bootstrap(session); } } }
void tmate_dispatch_slave_message(struct tmate_session *session, struct tmate_unpacker *uk) { int cmd = unpack_int(uk); switch (cmd) { #define dispatch(c, f) case c: f(session, uk); break dispatch(TMATE_IN_NOTIFY, handle_notify); dispatch(TMATE_IN_LEGACY_PANE_KEY, handle_legacy_pane_key); dispatch(TMATE_IN_RESIZE, handle_resize); dispatch(TMATE_IN_EXEC_CMD_STR, handle_exec_cmd_str); dispatch(TMATE_IN_SET_ENV, handle_set_env); dispatch(TMATE_IN_READY, handle_ready); dispatch(TMATE_IN_PANE_KEY, handle_pane_key); dispatch(TMATE_IN_EXEC_CMD, handle_exec_cmd); default: tmate_info("Bad message type: %d", cmd); } }
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_info("%s(%s) [%s]", file, line, address); return 0; }
static void lookup_and_connect(void) { struct evutil_addrinfo hints; if (!ev_dnsbase) ev_dnsbase = evdns_base_new(ev_base, 1); if (!ev_dnsbase) tmate_fatal("Cannot initialize the DNS lookup service"); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = 0; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; tmate_info("Looking up %s...", TMATE_HOST); (void)evdns_getaddrinfo(ev_dnsbase, TMATE_HOST, NULL, &hints, dns_cb, NULL); }
static void lookup_and_connect(void) { struct evutil_addrinfo hints; const char *tmate_server_host; if (!tmate_session.ev_dnsbase) tmate_session.ev_dnsbase = evdns_base_new(tmate_session.ev_base, 1); if (!tmate_session.ev_dnsbase) tmate_fatal("Cannot initialize the DNS lookup service"); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = EVUTIL_AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; tmate_server_host = options_get_string(global_options, "tmate-server-host"); tmate_info("Looking up %s...", tmate_server_host); (void)evdns_getaddrinfo(tmate_session.ev_dnsbase, tmate_server_host, NULL, &hints, dns_cb, (void *)tmate_server_host); }
static void handle_sigsegv(__unused int sig) { tmate_info("CRASH, printing stack trace"); tmate_print_stack_trace(); tmate_fatal("CRASHED"); }
static void client_bootstrap(struct tmate_session *_session) { struct tmate_ssh_client *client = &_session->ssh_client; int grace_period = TMATE_SSH_GRACE_PERIOD; ssh_event mainloop; ssh_session session = client->session; tmate_notice("Bootstrapping ssh client ip=%s", client->ip_address); _session->ev_base = osdep_event_init(); /* new process group, we don't want to die with our parent (upstart) */ setpgid(0, 0); { int flag = 1; setsockopt(ssh_get_fd(session), IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); } signal(SIGALRM, handle_sigalrm); alarm(grace_period); /* * We should die early if we can't connect to proxy. This way the * tmate daemon will pick another server to work on. */ _session->proxy_fd = -1; if (tmate_has_proxy()) _session->proxy_fd = tmate_connect_to_proxy(); ssh_server_cb.userdata = client; ssh_callbacks_init(&ssh_server_cb); ssh_set_server_callbacks(client->session, &ssh_server_cb); ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &grace_period); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); ssh_set_auth_methods(client->session, SSH_AUTH_METHOD_PUBLICKEY); tmate_info("Exchanging DH keys"); if (ssh_handle_key_exchange(session) < 0) tmate_fatal("Error doing the key exchange: %s", ssh_get_error(session)); mainloop = ssh_event_new(); ssh_event_add_session(mainloop, session); while (!client->role) { if (ssh_event_dopoll(mainloop, -1) == SSH_ERROR) tmate_fatal("Error polling ssh socket: %s", ssh_get_error(session)); } alarm(0); /* The latency is callback set later */ tmate_start_ssh_latency_probes(client, &ssh_server_cb, TMATE_SSH_KEEPALIVE * 1000); register_on_ssh_read(client); tmate_spawn(_session); /* never reached */ }
static void ssh_log_function(int priority, const char *function, const char *buffer, __unused void *userdata) { tmate_info("[%d] [%s] %s", priority, function, buffer); }