From 2fdb4c0c03c4f7ebf458389258dcaa4d85b424a5 Mon Sep 17 00:00:00 2001 From: Shuanglei Tao Date: Sat, 14 Mar 2020 17:41:06 +0800 Subject: [PATCH] src: reformat with clang-format --- .clang-format | 2 + src/http.c | 473 ++++++++++++------------- src/protocol.c | 811 +++++++++++++++++++++--------------------- src/server.c | 930 +++++++++++++++++++++++++------------------------ src/server.h | 104 +++--- src/terminal.c | 63 ++-- src/terminal.h | 8 +- src/utils.c | 176 +++++----- src/utils.h | 29 +- 9 files changed, 1273 insertions(+), 1323 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a327123 --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Google +Language: Cpp diff --git a/src/http.c b/src/http.c index deefe92..70b7368 100644 --- a/src/http.c +++ b/src/http.c @@ -1,282 +1,269 @@ -#include #include #include +#include #include -#include "server.h" #include "html.h" +#include "server.h" #include "utils.h" -enum { - AUTH_OK, AUTH_FAIL, AUTH_ERROR -}; +enum { AUTH_OK, AUTH_FAIL, AUTH_ERROR }; -static char * html_cache = NULL; +static char *html_cache = NULL; static size_t html_cache_len = 0; -static int -check_auth(struct lws *wsi, struct pss_http *pss) { - if (server->credential == NULL) - return AUTH_OK; - - int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION); - char buf[hdr_length + 1]; - int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_AUTHORIZATION); - if (len > 0) { - // extract base64 text from authorization header - char *ptr = &buf[0]; - char *token, *b64_text = NULL; - int i = 1; - while ((token = strsep(&ptr, " ")) != NULL) { - if (strlen(token) == 0) - continue; - if (i++ == 2) { - b64_text = token; - break; - } - } - if (b64_text != NULL && !strcmp(b64_text, server->credential)) - return AUTH_OK; +static int check_auth(struct lws *wsi, struct pss_http *pss) { + if (server->credential == NULL) return AUTH_OK; + + int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION); + char buf[hdr_length + 1]; + int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_AUTHORIZATION); + if (len > 0) { + // extract base64 text from authorization header + char *ptr = &buf[0]; + char *token, *b64_text = NULL; + int i = 1; + while ((token = strsep(&ptr, " ")) != NULL) { + if (strlen(token) == 0) continue; + if (i++ == 2) { + b64_text = token; + break; + } } - - unsigned char buffer[1024 + LWS_PRE], *p, *end; - p = buffer + LWS_PRE; - end = p + sizeof(buffer) - LWS_PRE; - - char *body = strdup("401 Unauthorized\n"); - size_t n = strlen(body); - - if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end)) - return AUTH_ERROR; - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_WWW_AUTHENTICATE, - (unsigned char *) "Basic realm=\"ttyd\"", - 18, &p, end)) - return AUTH_ERROR; - if (lws_add_http_header_content_length(wsi, n, &p, end)) - return AUTH_ERROR; - if (lws_finalize_http_header(wsi, &p, end)) - return AUTH_ERROR; - if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0) - return AUTH_ERROR; - - pss->buffer = pss->ptr = body; - pss->len = n; - lws_callback_on_writable(wsi); - - return AUTH_FAIL; + if (b64_text != NULL && !strcmp(b64_text, server->credential)) + return AUTH_OK; + } + + unsigned char buffer[1024 + LWS_PRE], *p, *end; + p = buffer + LWS_PRE; + end = p + sizeof(buffer) - LWS_PRE; + + char *body = strdup("401 Unauthorized\n"); + size_t n = strlen(body); + + if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end)) + return AUTH_ERROR; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_WWW_AUTHENTICATE, + (unsigned char *)"Basic realm=\"ttyd\"", 18, + &p, end)) + return AUTH_ERROR; + if (lws_add_http_header_content_length(wsi, n, &p, end)) return AUTH_ERROR; + if (lws_finalize_http_header(wsi, &p, end)) return AUTH_ERROR; + if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), + LWS_WRITE_HTTP_HEADERS) < 0) + return AUTH_ERROR; + + pss->buffer = pss->ptr = body; + pss->len = n; + lws_callback_on_writable(wsi); + + return AUTH_FAIL; } -static bool -accept_gzip(struct lws *wsi) { - int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING); - char buf[hdr_length + 1]; - int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_ACCEPT_ENCODING); - return len > 0 && strstr(buf, "gzip") != NULL; +static bool accept_gzip(struct lws *wsi) { + int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING); + char buf[hdr_length + 1]; + int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_ACCEPT_ENCODING); + return len > 0 && strstr(buf, "gzip") != NULL; } -static bool -uncompress_html(char **output, size_t *output_len) { - if (html_cache == NULL || html_cache_len == 0) { - z_stream stream; - memset(&stream, 0, sizeof(stream)); - if (inflateInit2(&stream, 16 + 15) != Z_OK) - return false; - - html_cache_len = index_html_size; - html_cache = xmalloc(html_cache_len); - - stream.avail_in = index_html_len; - stream.avail_out = html_cache_len; - stream.next_in = (void *) index_html; - stream.next_out = (void *) html_cache; - - int ret = inflate(&stream, Z_SYNC_FLUSH); - inflateEnd(&stream); - if (ret != Z_STREAM_END) { - free(html_cache); - html_cache = NULL; - html_cache_len = 0; - return false; - } +static bool uncompress_html(char **output, size_t *output_len) { + if (html_cache == NULL || html_cache_len == 0) { + z_stream stream; + memset(&stream, 0, sizeof(stream)); + if (inflateInit2(&stream, 16 + 15) != Z_OK) return false; + + html_cache_len = index_html_size; + html_cache = xmalloc(html_cache_len); + + stream.avail_in = index_html_len; + stream.avail_out = html_cache_len; + stream.next_in = (void *)index_html; + stream.next_out = (void *)html_cache; + + int ret = inflate(&stream, Z_SYNC_FLUSH); + inflateEnd(&stream); + if (ret != Z_STREAM_END) { + free(html_cache); + html_cache = NULL; + html_cache_len = 0; + return false; } + } - *output = html_cache; - *output_len = html_cache_len; + *output = html_cache; + *output_len = html_cache_len; - return true; + return true; } -static void -pss_buffer_free(struct pss_http *pss) { - if (pss->buffer != (char *) index_html && pss->buffer != html_cache) - free(pss->buffer); +static void pss_buffer_free(struct pss_http *pss) { + if (pss->buffer != (char *)index_html && pss->buffer != html_cache) + free(pss->buffer); } -static void -access_log(struct lws *wsi, const char *path) { - char rip[50]; +static void access_log(struct lws *wsi, const char *path) { + char rip[50]; #if LWS_LIBRARY_VERSION_NUMBER >= 2004000 - lws_get_peer_simple(lws_get_network_wsi(wsi), rip, sizeof(rip)); + lws_get_peer_simple(lws_get_network_wsi(wsi), rip, sizeof(rip)); #else - char name[100]; - lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), rip, sizeof(rip)); + char name[100]; + lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), rip, + sizeof(rip)); #endif - lwsl_notice("HTTP %s - %s\n", path, rip); + lwsl_notice("HTTP %s - %s\n", path, rip); } -int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - struct pss_http *pss = (struct pss_http *) user; - unsigned char buffer[4096 + LWS_PRE], *p, *end; - char buf[256]; - bool done = false; - - switch (reason) { - case LWS_CALLBACK_HTTP: - access_log(wsi, (const char *) in); - snprintf(pss->path, sizeof(pss->path), "%s", (const char *) in); - switch (check_auth(wsi, pss)) { - case AUTH_OK: - break; - case AUTH_FAIL: - return 0; - case AUTH_ERROR: - default: - return 1; - } - - p = buffer + LWS_PRE; - end = p + sizeof(buffer) - LWS_PRE; - - if (strcmp(pss->path, endpoints.token) == 0) { - const char *credential = server->credential != NULL ? server->credential : ""; - size_t n = sprintf(buf, "{\"token\": \"%s\"}", credential); - if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) - return 1; - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *) "application/json;charset=utf-8", - 30, &p, end)) - return 1; - if (lws_add_http_header_content_length(wsi, (unsigned long) n, &p, end)) - return 1; - if (lws_finalize_http_header(wsi, &p, end)) - return 1; - if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0) - return 1; - pss->buffer = pss->ptr = strdup(buf); - pss->len = n; - lws_callback_on_writable(wsi); - break; - } - - if (strcmp(pss->path, endpoints.index) != 0) { - lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); - goto try_to_reuse; - } - - const char* content_type = "text/html"; - if (server->index != NULL) { - int n = lws_serve_http_file(wsi, server->index, content_type, NULL, 0); - if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) - return 1; - } else { - char *output = (char *) index_html; - size_t output_len = index_html_len; - if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) - return 1; - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (const unsigned char *) content_type, 9, &p, end)) - return 1; +int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) { + struct pss_http *pss = (struct pss_http *)user; + unsigned char buffer[4096 + LWS_PRE], *p, *end; + char buf[256]; + bool done = false; + + switch (reason) { + case LWS_CALLBACK_HTTP: + access_log(wsi, (const char *)in); + snprintf(pss->path, sizeof(pss->path), "%s", (const char *)in); + switch (check_auth(wsi, pss)) { + case AUTH_OK: + break; + case AUTH_FAIL: + return 0; + case AUTH_ERROR: + default: + return 1; + } + + p = buffer + LWS_PRE; + end = p + sizeof(buffer) - LWS_PRE; + + if (strcmp(pss->path, endpoints.token) == 0) { + const char *credential = + server->credential != NULL ? server->credential : ""; + size_t n = sprintf(buf, "{\"token\": \"%s\"}", credential); + if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) return 1; + if (lws_add_http_header_by_token( + wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"application/json;charset=utf-8", 30, &p, end)) + return 1; + if (lws_add_http_header_content_length(wsi, (unsigned long)n, &p, end)) + return 1; + if (lws_finalize_http_header(wsi, &p, end)) return 1; + if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), + LWS_WRITE_HTTP_HEADERS) < 0) + return 1; + pss->buffer = pss->ptr = strdup(buf); + pss->len = n; + lws_callback_on_writable(wsi); + break; + } + + if (strcmp(pss->path, endpoints.index) != 0) { + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); + goto try_to_reuse; + } + + const char *content_type = "text/html"; + if (server->index != NULL) { + int n = lws_serve_http_file(wsi, server->index, content_type, NULL, 0); + if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) return 1; + } else { + char *output = (char *)index_html; + size_t output_len = index_html_len; + if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) return 1; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (const unsigned char *)content_type, 9, + &p, end)) + return 1; #ifdef LWS_WITH_HTTP_STREAM_COMPRESSION - if (!uncompress_html(&output, &output_len)) - return 1; + if (!uncompress_html(&output, &output_len)) return 1; #else - if (accept_gzip(wsi)) { - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING, (unsigned char *) "gzip", 4, &p, end)) - return 1; - } else { - if (!uncompress_html(&output, &output_len)) - return 1; - } + if (accept_gzip(wsi)) { + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING, + (unsigned char *)"gzip", 4, &p, end)) + return 1; + } else { + if (!uncompress_html(&output, &output_len)) return 1; + } #endif - if (lws_add_http_header_content_length(wsi, (unsigned long) output_len, &p, end)) - return 1; + if (lws_add_http_header_content_length(wsi, (unsigned long)output_len, + &p, end)) + return 1; - if (lws_finalize_http_header(wsi, &p, end)) - return 1; - if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0) - return 1; + if (lws_finalize_http_header(wsi, &p, end)) return 1; + if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), + LWS_WRITE_HTTP_HEADERS) < 0) + return 1; #if LWS_LIBRARY_VERSION_MAJOR < 2 - if (lws_write_http(wsi, output, output_len) < 0) - return 1; - goto try_to_reuse; + if (lws_write_http(wsi, output, output_len) < 0) return 1; + goto try_to_reuse; #else - pss->buffer = pss->ptr = output; - pss->len = output_len; - lws_callback_on_writable(wsi); + pss->buffer = pss->ptr = output; + pss->len = output_len; + lws_callback_on_writable(wsi); #endif - } - break; - - case LWS_CALLBACK_HTTP_WRITEABLE: - if (!pss->buffer || pss->len <= 0) { - goto try_to_reuse; - } - - do { - int n = sizeof(buffer) - LWS_PRE; - int m = lws_get_peer_write_allowance(wsi); - if (m == 0) { - lws_callback_on_writable(wsi); - return 0; - } else if (m != -1 && m < n) { - n = m; - } - if (pss->ptr + n > pss->buffer + pss->len) { - n = (int) (pss->len - (pss->ptr - pss->buffer)); - done = true; - } - memcpy(buffer + LWS_PRE, pss->ptr, n); - pss->ptr += n; - if (lws_write_http(wsi, buffer + LWS_PRE, (size_t) n) < n) { - pss_buffer_free(pss); - return -1; - } - } while (!lws_send_pipe_choked(wsi) && !done); - - if (!done && pss->ptr < pss->buffer + pss->len) { - lws_callback_on_writable(wsi); - break; - } - - pss_buffer_free(pss); - goto try_to_reuse; - - case LWS_CALLBACK_HTTP_FILE_COMPLETION: - goto try_to_reuse; - - case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: - if (!len || (SSL_get_verify_result((SSL *) in) != X509_V_OK)) { - int err = X509_STORE_CTX_get_error((X509_STORE_CTX *) user); - int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX *) user); - const char *msg = X509_verify_cert_error_string(err); - lwsl_err("client certificate verification error: %s (%d), depth: %d\n", msg, err, depth); - return 1; - } - break; - default: - break; - } - - return 0; - - /* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */ + } + break; + + case LWS_CALLBACK_HTTP_WRITEABLE: + if (!pss->buffer || pss->len <= 0) { + goto try_to_reuse; + } + + do { + int n = sizeof(buffer) - LWS_PRE; + int m = lws_get_peer_write_allowance(wsi); + if (m == 0) { + lws_callback_on_writable(wsi); + return 0; + } else if (m != -1 && m < n) { + n = m; + } + if (pss->ptr + n > pss->buffer + pss->len) { + n = (int)(pss->len - (pss->ptr - pss->buffer)); + done = true; + } + memcpy(buffer + LWS_PRE, pss->ptr, n); + pss->ptr += n; + if (lws_write_http(wsi, buffer + LWS_PRE, (size_t)n) < n) { + pss_buffer_free(pss); + return -1; + } + } while (!lws_send_pipe_choked(wsi) && !done); + + if (!done && pss->ptr < pss->buffer + pss->len) { + lws_callback_on_writable(wsi); + break; + } + + pss_buffer_free(pss); + goto try_to_reuse; + + case LWS_CALLBACK_HTTP_FILE_COMPLETION: + goto try_to_reuse; + + case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: + if (!len || (SSL_get_verify_result((SSL *)in) != X509_V_OK)) { + int err = X509_STORE_CTX_get_error((X509_STORE_CTX *)user); + int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX *)user); + const char *msg = X509_verify_cert_error_string(err); + lwsl_err("client certificate verification error: %s (%d), depth: %d\n", + msg, err, depth); + return 1; + } + break; + default: + break; + } + + return 0; + + /* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */ try_to_reuse: - if (lws_http_transaction_completed(wsi)) - return -1; + if (lws_http_transaction_completed(wsi)) return -1; - return 0; + return 0; } diff --git a/src/protocol.c b/src/protocol.c index 9949759..4a1fc0d 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -1,468 +1,459 @@ +#include +#include +#include +#include #include #include #include #include -#include -#include #include -#include -#include - #include "server.h" #include "terminal.h" #include "utils.h" // initial message list -static char initial_cmds[] = { - SET_WINDOW_TITLE, - SET_PREFERENCES -}; - -static int -send_initial_message(struct lws *wsi, int index) { - unsigned char message[LWS_PRE + 1 + 4096]; - unsigned char *p = &message[LWS_PRE]; - char buffer[128]; - int n = 0; - - char cmd = initial_cmds[index]; - switch(cmd) { - case SET_WINDOW_TITLE: - gethostname(buffer, sizeof(buffer) - 1); - n = sprintf((char *) p, "%c%s (%s)", cmd, server->command, buffer); - break; - case SET_PREFERENCES: - n = sprintf((char *) p, "%c%s", cmd, server->prefs_json); - break; - default: - break; - } - - return lws_write(wsi, p, (size_t) n, LWS_WRITE_BINARY); +static char initial_cmds[] = {SET_WINDOW_TITLE, SET_PREFERENCES}; + +static int send_initial_message(struct lws *wsi, int index) { + unsigned char message[LWS_PRE + 1 + 4096]; + unsigned char *p = &message[LWS_PRE]; + char buffer[128]; + int n = 0; + + char cmd = initial_cmds[index]; + switch (cmd) { + case SET_WINDOW_TITLE: + gethostname(buffer, sizeof(buffer) - 1); + n = sprintf((char *)p, "%c%s (%s)", cmd, server->command, buffer); + break; + case SET_PREFERENCES: + n = sprintf((char *)p, "%c%s", cmd, server->prefs_json); + break; + default: + break; + } + + return lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY); } -static bool -parse_window_size(struct pss_tty *pss, int *cols, int *rows) { - char json[pss->len]; - strncpy(json, pss->buffer + 1, pss->len - 1); - json[pss->len-1] = '\0'; - - json_object *obj = json_tokener_parse(json); - struct json_object *o = NULL; - - if (!json_object_object_get_ex(obj, "columns", &o)) { - lwsl_err("columns field not exists, json: %s\n", json); - return false; - } - *cols = json_object_get_int(o); - if (!json_object_object_get_ex(obj, "rows", &o)) { - lwsl_err("rows field not exists, json: %s\n", json); - return false; - } - *rows = json_object_get_int(o); - json_object_put(obj); - - return true; +static bool parse_window_size(struct pss_tty *pss, int *cols, int *rows) { + char json[pss->len]; + strncpy(json, pss->buffer + 1, pss->len - 1); + json[pss->len - 1] = '\0'; + + json_object *obj = json_tokener_parse(json); + struct json_object *o = NULL; + + if (!json_object_object_get_ex(obj, "columns", &o)) { + lwsl_err("columns field not exists, json: %s\n", json); + return false; + } + *cols = json_object_get_int(o); + if (!json_object_object_get_ex(obj, "rows", &o)) { + lwsl_err("rows field not exists, json: %s\n", json); + return false; + } + *rows = json_object_get_int(o); + json_object_put(obj); + + return true; } -static bool -check_host_origin(struct lws *wsi) { - int origin_length = lws_hdr_total_length(wsi, WSI_TOKEN_ORIGIN); - char buf[origin_length + 1]; - memset(buf, 0, sizeof(buf)); - int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_ORIGIN); - if (len <= 0) { - return false; - } - - const char *prot, *address, *path; - int port; - if (lws_parse_uri(buf, &prot, &address, &port, &path)) - return false; - if (port == 80 || port == 443) { - sprintf(buf, "%s", address); - } else { - sprintf(buf, "%s:%d", address, port); - } - - int host_length = lws_hdr_total_length(wsi, WSI_TOKEN_HOST); - if (host_length != strlen(buf)) - return false; - char host_buf[host_length + 1]; - memset(host_buf, 0, sizeof(host_buf)); - len = lws_hdr_copy(wsi, host_buf, sizeof(host_buf), WSI_TOKEN_HOST); - - return len > 0 && strcasecmp(buf, host_buf) == 0; +static bool check_host_origin(struct lws *wsi) { + int origin_length = lws_hdr_total_length(wsi, WSI_TOKEN_ORIGIN); + char buf[origin_length + 1]; + memset(buf, 0, sizeof(buf)); + int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_ORIGIN); + if (len <= 0) { + return false; + } + + const char *prot, *address, *path; + int port; + if (lws_parse_uri(buf, &prot, &address, &port, &path)) return false; + if (port == 80 || port == 443) { + sprintf(buf, "%s", address); + } else { + sprintf(buf, "%s:%d", address, port); + } + + int host_length = lws_hdr_total_length(wsi, WSI_TOKEN_HOST); + if (host_length != strlen(buf)) return false; + char host_buf[host_length + 1]; + memset(host_buf, 0, sizeof(host_buf)); + len = lws_hdr_copy(wsi, host_buf, sizeof(host_buf), WSI_TOKEN_HOST); + + return len > 0 && strcasecmp(buf, host_buf) == 0; } -static void -pty_proc_free(struct pty_proc *proc) { - uv_read_stop((uv_stream_t *) &proc->pipe); - uv_close((uv_handle_t*) &proc->pipe, NULL); +static void pty_proc_free(struct pty_proc *proc) { + uv_read_stop((uv_stream_t *)&proc->pipe); + uv_close((uv_handle_t *)&proc->pipe, NULL); - close(proc->pty); + close(proc->pty); - if (proc->pty_buffer != NULL) { - free(proc->pty_buffer); - proc->pty_buffer = NULL; - } + if (proc->pty_buffer != NULL) { + free(proc->pty_buffer); + proc->pty_buffer = NULL; + } - for (int i = 0; i < proc->argc; i++) { - free(proc->args[i]); - } + for (int i = 0; i < proc->argc; i++) { + free(proc->args[i]); + } - free(proc); + free(proc); } -static void -alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - buf->base = xmalloc(suggested_size); - buf->len = suggested_size; +static void alloc_cb(uv_handle_t *handle, size_t suggested_size, + uv_buf_t *buf) { + buf->base = xmalloc(suggested_size); + buf->len = suggested_size; } -static void -read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - struct pss_tty *pss = (struct pss_tty *) stream->data; - struct pty_proc *proc = pss->proc; - proc->pty_len = nread; - - uv_read_stop(stream); - - if (nread <= 0) { - if (nread == UV_ENOBUFS || nread == 0) - return; - proc->pty_buffer = NULL; - if (nread == UV_EOF) - proc->pty_len = 0; - else - lwsl_err("read_cb: %s\n", uv_err_name(nread)); - } else { - proc->pty_buffer = xmalloc(LWS_PRE + 1 + (size_t ) nread); - memcpy(proc->pty_buffer + LWS_PRE + 1, buf->base, (size_t ) nread); - } - free(buf->base); - - lws_callback_on_writable(pss->wsi); +static void read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { + struct pss_tty *pss = (struct pss_tty *)stream->data; + struct pty_proc *proc = pss->proc; + proc->pty_len = nread; + + uv_read_stop(stream); + + if (nread <= 0) { + if (nread == UV_ENOBUFS || nread == 0) return; + proc->pty_buffer = NULL; + if (nread == UV_EOF) + proc->pty_len = 0; + else + lwsl_err("read_cb: %s\n", uv_err_name(nread)); + } else { + proc->pty_buffer = xmalloc(LWS_PRE + 1 + (size_t)nread); + memcpy(proc->pty_buffer + LWS_PRE + 1, buf->base, (size_t)nread); + } + free(buf->base); + + lws_callback_on_writable(pss->wsi); } -static void -child_cb(uv_signal_t *handle, int signum) { - pid_t pid; - int stat; - - struct pty_proc *proc; - LIST_HEAD(proc, pty_proc) *procs = handle->data; - LIST_FOREACH(proc, procs, entry) { - do - pid = waitpid(proc->pid, &stat, WNOHANG); - while (pid == -1 && errno == EINTR); - - if (pid <= 0) - continue; - - if (WIFEXITED(stat)) { - proc->status = WEXITSTATUS(stat); - lwsl_notice("process exited with code %d, pid: %d\n", proc->status, proc->pid); - } else if (WIFSIGNALED(stat)) { - int sig = WTERMSIG(stat); - char sig_name[20]; - - proc->status = 128 + sig; - get_sig_name(sig, sig_name, sizeof(sig_name)); - lwsl_notice("process killed with signal %d (%s), pid: %d\n", sig, sig_name, proc->pid); - } +static void child_cb(uv_signal_t *handle, int signum) { + pid_t pid; + int stat; + + struct pty_proc *proc; + LIST_HEAD(proc, pty_proc) *procs = handle->data; + LIST_FOREACH(proc, procs, entry) { + do + pid = waitpid(proc->pid, &stat, WNOHANG); + while (pid == -1 && errno == EINTR); + + if (pid <= 0) continue; + + if (WIFEXITED(stat)) { + proc->status = WEXITSTATUS(stat); + lwsl_notice("process exited with code %d, pid: %d\n", proc->status, + proc->pid); + } else if (WIFSIGNALED(stat)) { + int sig = WTERMSIG(stat); + char sig_name[20]; + + proc->status = 128 + sig; + get_sig_name(sig, sig_name, sizeof(sig_name)); + lwsl_notice("process killed with signal %d (%s), pid: %d\n", sig, + sig_name, proc->pid); + } - LIST_REMOVE(proc, entry); - if (proc->state == STATE_KILL) { - pty_proc_free(proc); - } else { - proc->state = STATE_EXIT; - } + LIST_REMOVE(proc, entry); + if (proc->state == STATE_KILL) { + pty_proc_free(proc); + } else { + proc->state = STATE_EXIT; } + } } -static int -spawn_process(struct pss_tty *pss) { - struct pty_proc *proc = pss->proc; - // append url args to arguments - char *argv[server->argc + proc->argc + 1]; - int i, n = 0; - for (i = 0; i < server->argc; i++) { - argv[n++] = server->argv[i]; - } - for (i = 0; i < proc->argc; i++) { - argv[n++] = proc->args[i]; - } - argv[n] = NULL; +static int spawn_process(struct pss_tty *pss) { + struct pty_proc *proc = pss->proc; + // append url args to arguments + char *argv[server->argc + proc->argc + 1]; + int i, n = 0; + for (i = 0; i < server->argc; i++) { + argv[n++] = server->argv[i]; + } + for (i = 0; i < proc->argc; i++) { + argv[n++] = proc->args[i]; + } + argv[n] = NULL; - uv_signal_start(&server->watcher, child_cb, SIGCHLD); + uv_signal_start(&server->watcher, child_cb, SIGCHLD); - // ensure the lws socket fd close-on-exec - fd_set_cloexec(lws_get_socket_fd(pss->wsi)); + // ensure the lws socket fd close-on-exec + fd_set_cloexec(lws_get_socket_fd(pss->wsi)); - // create process with pseudo-tty - proc->pid = pty_fork(&proc->pty, argv[0], argv, server->terminal_type); - if (proc->pid < 0) { - lwsl_err("pty_fork: %d (%s)\n", errno, strerror(errno)); - return 1; - } + // create process with pseudo-tty + proc->pid = pty_fork(&proc->pty, argv[0], argv, server->terminal_type); + if (proc->pid < 0) { + lwsl_err("pty_fork: %d (%s)\n", errno, strerror(errno)); + return 1; + } - lwsl_notice("started process, pid: %d\n", proc->pid); + lwsl_notice("started process, pid: %d\n", proc->pid); - proc->pipe.data = pss; - uv_pipe_open(&proc->pipe, proc->pty); + proc->pipe.data = pss; + uv_pipe_open(&proc->pipe, proc->pty); - lws_callback_on_writable(pss->wsi); + lws_callback_on_writable(pss->wsi); - return 0; + return 0; } -static void -kill_process(struct pty_proc *proc) { - if (proc->pid <= 0) return; +static void kill_process(struct pty_proc *proc) { + if (proc->pid <= 0) return; - pid_t pid = proc->pid; - int sig = server->sig_code; - char *sig_name = server->sig_name; + pid_t pid = proc->pid; + int sig = server->sig_code; + char *sig_name = server->sig_name; - lwsl_notice("killing process %d with signal: %d (%s)\n", pid, sig, sig_name); - int pgid = getpgid(pid); - if (uv_kill(pgid > 0 ? -pgid : pid, sig) != 0) { - lwsl_err("kill: %d, errno: %d (%s)\n", pid, errno, strerror(errno)); - } + lwsl_notice("killing process %d with signal: %d (%s)\n", pid, sig, sig_name); + int pgid = getpgid(pid); + if (uv_kill(pgid > 0 ? -pgid : pid, sig) != 0) { + lwsl_err("kill: %d, errno: %d (%s)\n", pid, errno, strerror(errno)); + } } -static void -write_cb(uv_write_t* req, int status) { - if (status != 0) - lwsl_warn("uv_write callback returned status: %d\n", status); - free(req->data); - free(req); +static void write_cb(uv_write_t *req, int status) { + if (status != 0) lwsl_warn("uv_write callback returned status: %d\n", status); + free(req->data); + free(req); } -int -callback_tty(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) { - struct pss_tty *pss = (struct pss_tty *) user; - struct pty_proc *proc; - char buf[256]; - size_t n = 0; - - switch (reason) { - case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: - if (server->once && server->client_count > 0) { - lwsl_warn("refuse to serve WS client due to the --once option.\n"); - return 1; - } - if (server->max_clients > 0 && server->client_count == server->max_clients) { - lwsl_warn("refuse to serve WS client due to the --max-clients option.\n"); - return 1; - } - if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI) <= 0 || strcmp(buf, endpoints.ws) != 0) { - lwsl_warn("refuse to serve WS client for illegal ws path: %s\n", buf); - return 1; - } +int callback_tty(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) { + struct pss_tty *pss = (struct pss_tty *)user; + struct pty_proc *proc; + char buf[256]; + size_t n = 0; + + switch (reason) { + case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: + if (server->once && server->client_count > 0) { + lwsl_warn("refuse to serve WS client due to the --once option.\n"); + return 1; + } + if (server->max_clients > 0 && + server->client_count == server->max_clients) { + lwsl_warn( + "refuse to serve WS client due to the --max-clients option.\n"); + return 1; + } + if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI) <= 0 || + strcmp(buf, endpoints.ws) != 0) { + lwsl_warn("refuse to serve WS client for illegal ws path: %s\n", buf); + return 1; + } - if (server->check_origin && !check_host_origin(wsi)) { - lwsl_warn("refuse to serve WS client from different origin due to the --check-origin option.\n"); - return 1; - } - break; - - case LWS_CALLBACK_ESTABLISHED: - pss->initialized = false; - pss->initial_cmd_index = 0; - pss->authenticated = false; - pss->wsi = wsi; - pss->buffer = NULL; - - pss->proc = proc = xmalloc(sizeof(struct pty_proc)); - memset(proc, 0, sizeof(struct pty_proc)); - proc->status = -1; - proc->state = STATE_INIT; - uv_pipe_init(server->loop, &proc->pipe, 0); - - if (server->url_arg) { - while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_URI_ARGS, n++) > 0) { - if (strncmp(buf, "arg=", 4) == 0) { - proc->args = xrealloc(proc->args, (proc->argc + 1) * sizeof(char *)); - proc->args[proc->argc] = strdup(&buf[4]); - proc->argc++; - } - } - } + if (server->check_origin && !check_host_origin(wsi)) { + lwsl_warn( + "refuse to serve WS client from different origin due to the " + "--check-origin option.\n"); + return 1; + } + break; + + case LWS_CALLBACK_ESTABLISHED: + pss->initialized = false; + pss->initial_cmd_index = 0; + pss->authenticated = false; + pss->wsi = wsi; + pss->buffer = NULL; + + pss->proc = proc = xmalloc(sizeof(struct pty_proc)); + memset(proc, 0, sizeof(struct pty_proc)); + proc->status = -1; + proc->state = STATE_INIT; + uv_pipe_init(server->loop, &proc->pipe, 0); + + if (server->url_arg) { + while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), + WSI_TOKEN_HTTP_URI_ARGS, n++) > 0) { + if (strncmp(buf, "arg=", 4) == 0) { + proc->args = + xrealloc(proc->args, (proc->argc + 1) * sizeof(char *)); + proc->args[proc->argc] = strdup(&buf[4]); + proc->argc++; + } + } + } - LIST_INSERT_HEAD(&server->procs, proc, entry); - server->client_count++; + LIST_INSERT_HEAD(&server->procs, proc, entry); + server->client_count++; - lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI); + lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI); #if LWS_LIBRARY_VERSION_NUMBER >= 2004000 - lws_get_peer_simple(lws_get_network_wsi(wsi), pss->address, sizeof(pss->address)); + lws_get_peer_simple(lws_get_network_wsi(wsi), pss->address, + sizeof(pss->address)); #else - char name[100]; - lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), pss->address, sizeof(pss->address)); + char name[100]; + lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), + pss->address, sizeof(pss->address)); #endif - lwsl_notice("WS %s - %s, clients: %d\n", buf, pss->address, server->client_count); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - proc = pss->proc; - if (!pss->initialized) { - if (pss->initial_cmd_index == sizeof(initial_cmds)) { - pss->initialized = true; - uv_read_start((uv_stream_t *)& proc->pipe, alloc_cb, read_cb); - break; - } - if (send_initial_message(wsi, pss->initial_cmd_index) < 0) { - lwsl_err("failed to send initial message, index: %d\n", pss->initial_cmd_index); - lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0); - return -1; - } - pss->initial_cmd_index++; - lws_callback_on_writable(wsi); - break; - } - - // read error or client exited, close connection - if (proc->status == 0 || proc->pty_len == 0) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_NORMAL, NULL, 0); - return 1; - } else if (proc->status > 0 || proc->pty_len < 0) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0); - return -1; - } - - if (proc->pty_buffer == NULL) - break; - - proc->pty_buffer[LWS_PRE] = OUTPUT; - n = (size_t) (proc->pty_len + 1); - if (lws_write(wsi, (unsigned char *) proc->pty_buffer + LWS_PRE, n, LWS_WRITE_BINARY) < n) { - lwsl_err("write OUTPUT to WS\n"); - } - free(proc->pty_buffer); - proc->pty_buffer = NULL; - uv_read_start((uv_stream_t *)& proc->pipe, alloc_cb, read_cb); - break; - - case LWS_CALLBACK_RECEIVE: - if (pss->buffer == NULL) { - pss->buffer = xmalloc(len); - pss->len = len; - memcpy(pss->buffer, in, len); - } else { - pss->buffer = xrealloc(pss->buffer, pss->len + len); - memcpy(pss->buffer + pss->len, in, len); - pss->len += len; - } - - const char command = pss->buffer[0]; - - // check auth - if (server->credential != NULL && !pss->authenticated && command != JSON_DATA) { - lwsl_warn("WS client not authenticated\n"); - return 1; - } - - // check if there are more fragmented messages - if (lws_remaining_packet_payload(wsi) > 0 || !lws_is_final_fragment(wsi)) { - return 0; - } - - proc = pss->proc; - switch (command) { - case INPUT: - if (proc->pty == 0) - break; - if (server->readonly) - return 0; - - char *data = xmalloc(pss->len - 1); - memcpy(data, pss->buffer + 1, pss->len - 1); - - uv_buf_t b = { data, pss->len - 1 }; - uv_write_t *req = xmalloc(sizeof(uv_write_t)); - req->data = data; - - int err = uv_write(req, (uv_stream_t *) &proc->pipe, &b, 1, write_cb); - if (err) { - lwsl_err("uv_write: %s\n", uv_err_name(err)); - return -1; - } - break; - case RESIZE_TERMINAL: - { - int cols, rows; - if (parse_window_size(pss, &cols, &rows)) { - if (pty_resize(proc->pty, cols, rows) < 0) { - lwsl_err("pty_resize: %d (%s)\n", errno, strerror(errno)); - } - } - } - break; - case JSON_DATA: - if (proc->pid > 0) - break; - if (server->credential != NULL) { - json_object *obj = json_tokener_parse(pss->buffer); - struct json_object *o = NULL; - if (json_object_object_get_ex(obj, "AuthToken", &o)) { - const char *token = json_object_get_string(o); - if (token != NULL && !strcmp(token, server->credential)) - pss->authenticated = true; - else - lwsl_warn("WS authentication failed with token: %s\n", token); - } - if (!pss->authenticated) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, NULL, 0); - return -1; - } - } - if (spawn_process(pss) != 0) return 1; - break; - default: - lwsl_warn("ignored unknown message type: %c\n", command); - break; - } - - if (pss->buffer != NULL) { - free(pss->buffer); - pss->buffer = NULL; - } - break; - - case LWS_CALLBACK_CLOSED: - if (pss->wsi == NULL) - break; - - server->client_count--; - lwsl_notice("WS closed from %s, clients: %d\n", pss->address, server->client_count); - if (pss->buffer != NULL) { - free(pss->buffer); + lwsl_notice("WS %s - %s, clients: %d\n", buf, pss->address, + server->client_count); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + proc = pss->proc; + if (!pss->initialized) { + if (pss->initial_cmd_index == sizeof(initial_cmds)) { + pss->initialized = true; + uv_read_start((uv_stream_t *)&proc->pipe, alloc_cb, read_cb); + break; + } + if (send_initial_message(wsi, pss->initial_cmd_index) < 0) { + lwsl_err("failed to send initial message, index: %d\n", + pss->initial_cmd_index); + lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0); + return -1; + } + pss->initial_cmd_index++; + lws_callback_on_writable(wsi); + break; + } + + // read error or client exited, close connection + if (proc->status == 0 || proc->pty_len == 0) { + lws_close_reason(wsi, LWS_CLOSE_STATUS_NORMAL, NULL, 0); + return 1; + } else if (proc->status > 0 || proc->pty_len < 0) { + lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0); + return -1; + } + + if (proc->pty_buffer == NULL) break; + + proc->pty_buffer[LWS_PRE] = OUTPUT; + n = (size_t)(proc->pty_len + 1); + if (lws_write(wsi, (unsigned char *)proc->pty_buffer + LWS_PRE, n, + LWS_WRITE_BINARY) < n) { + lwsl_err("write OUTPUT to WS\n"); + } + free(proc->pty_buffer); + proc->pty_buffer = NULL; + uv_read_start((uv_stream_t *)&proc->pipe, alloc_cb, read_cb); + break; + + case LWS_CALLBACK_RECEIVE: + if (pss->buffer == NULL) { + pss->buffer = xmalloc(len); + pss->len = len; + memcpy(pss->buffer, in, len); + } else { + pss->buffer = xrealloc(pss->buffer, pss->len + len); + memcpy(pss->buffer + pss->len, in, len); + pss->len += len; + } + + const char command = pss->buffer[0]; + + // check auth + if (server->credential != NULL && !pss->authenticated && + command != JSON_DATA) { + lwsl_warn("WS client not authenticated\n"); + return 1; + } + + // check if there are more fragmented messages + if (lws_remaining_packet_payload(wsi) > 0 || + !lws_is_final_fragment(wsi)) { + return 0; + } + + proc = pss->proc; + switch (command) { + case INPUT: + if (proc->pty == 0) break; + if (server->readonly) return 0; + + char *data = xmalloc(pss->len - 1); + memcpy(data, pss->buffer + 1, pss->len - 1); + + uv_buf_t b = {data, pss->len - 1}; + uv_write_t *req = xmalloc(sizeof(uv_write_t)); + req->data = data; + + int err = uv_write(req, (uv_stream_t *)&proc->pipe, &b, 1, write_cb); + if (err) { + lwsl_err("uv_write: %s\n", uv_err_name(err)); + return -1; + } + break; + case RESIZE_TERMINAL: { + int cols, rows; + if (parse_window_size(pss, &cols, &rows)) { + if (pty_resize(proc->pty, cols, rows) < 0) { + lwsl_err("pty_resize: %d (%s)\n", errno, strerror(errno)); } - - proc = pss->proc; - if (proc->state == STATE_EXIT) { - pty_proc_free(proc); - } else { - proc->state = STATE_KILL; - uv_read_stop((uv_stream_t *) &proc->pipe); - kill_process(proc); + } + } break; + case JSON_DATA: + if (proc->pid > 0) break; + if (server->credential != NULL) { + json_object *obj = json_tokener_parse(pss->buffer); + struct json_object *o = NULL; + if (json_object_object_get_ex(obj, "AuthToken", &o)) { + const char *token = json_object_get_string(o); + if (token != NULL && !strcmp(token, server->credential)) + pss->authenticated = true; + else + lwsl_warn("WS authentication failed with token: %s\n", token); } - - if (server->once && server->client_count == 0) { - lwsl_notice("exiting due to the --once option.\n"); - force_exit = true; - lws_cancel_service(context); - exit(0); + if (!pss->authenticated) { + lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, NULL, 0); + return -1; } - break; - + } + if (spawn_process(pss) != 0) return 1; + break; default: - break; - } - - return 0; + lwsl_warn("ignored unknown message type: %c\n", command); + break; + } + + if (pss->buffer != NULL) { + free(pss->buffer); + pss->buffer = NULL; + } + break; + + case LWS_CALLBACK_CLOSED: + if (pss->wsi == NULL) break; + + server->client_count--; + lwsl_notice("WS closed from %s, clients: %d\n", pss->address, + server->client_count); + if (pss->buffer != NULL) { + free(pss->buffer); + } + + proc = pss->proc; + if (proc->state == STATE_EXIT) { + pty_proc_free(proc); + } else { + proc->state = STATE_KILL; + uv_read_stop((uv_stream_t *)&proc->pipe); + kill_process(proc); + } + + if (server->once && server->client_count == 0) { + lwsl_notice("exiting due to the --once option.\n"); + force_exit = true; + lws_cancel_service(context); + exit(0); + } + break; + + default: + break; + } + + return 0; } diff --git a/src/server.c b/src/server.c index 7a58d62..3debb14 100644 --- a/src/server.c +++ b/src/server.c @@ -1,16 +1,16 @@ -#include -#include -#include -#include +#include "server.h" + #include #include +#include +#include #include +#include +#include +#include +#include #include -#include -#include - -#include "server.h" #include "utils.h" #ifndef TTYD_VERSION @@ -22,533 +22,537 @@ struct lws_context *context; struct server *server; struct endpoints endpoints = {"/ws", "/", "/token"}; +extern int callback_http(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); +extern int callback_tty(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); + // websocket protocols static const struct lws_protocols protocols[] = { - {"http-only", callback_http, sizeof(struct pss_http), 0}, - {"tty", callback_tty, sizeof(struct pss_tty), 0}, - {NULL, NULL, 0, 0} -}; + {"http-only", callback_http, sizeof(struct pss_http), 0}, + {"tty", callback_tty, sizeof(struct pss_tty), 0}, + {NULL, NULL, 0, 0}}; #ifndef LWS_WITHOUT_EXTENSIONS // websocket extensions static const struct lws_extension extensions[] = { - {"permessage-deflate", lws_extension_callback_pm_deflate, "permessage-deflate"}, - {"deflate-frame", lws_extension_callback_pm_deflate, "deflate_frame"}, - {NULL, NULL, NULL} -}; + {"permessage-deflate", lws_extension_callback_pm_deflate, + "permessage-deflate"}, + {"deflate-frame", lws_extension_callback_pm_deflate, "deflate_frame"}, + {NULL, NULL, NULL}}; #endif // command line options static const struct option options[] = { - {"port", required_argument, NULL, 'p'}, - {"interface", required_argument, NULL, 'i'}, - {"credential", required_argument, NULL, 'c'}, - {"uid", required_argument, NULL, 'u'}, - {"gid", required_argument, NULL, 'g'}, - {"signal", required_argument, NULL, 's'}, - {"index", required_argument, NULL, 'I'}, - {"base-path", required_argument, NULL, 'b'}, - {"ipv6", no_argument, NULL, '6'}, - {"ssl", no_argument, NULL, 'S'}, - {"ssl-cert", required_argument, NULL, 'C'}, - {"ssl-key", required_argument, NULL, 'K'}, - {"ssl-ca", required_argument, NULL, 'A'}, - {"url-arg", no_argument, NULL, 'a'}, - {"readonly", no_argument, NULL, 'R'}, - {"terminal-type", required_argument, NULL, 'T'}, - {"client-option", required_argument, NULL, 't'}, - {"check-origin", no_argument, NULL, 'O'}, - {"max-clients", required_argument, NULL, 'm'}, - {"once", no_argument, NULL, 'o'}, - {"browser", no_argument, NULL, 'B'}, - {"debug", required_argument, NULL, 'd'}, - {"version", no_argument, NULL, 'v'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, 0, 0} -}; + {"port", required_argument, NULL, 'p'}, + {"interface", required_argument, NULL, 'i'}, + {"credential", required_argument, NULL, 'c'}, + {"uid", required_argument, NULL, 'u'}, + {"gid", required_argument, NULL, 'g'}, + {"signal", required_argument, NULL, 's'}, + {"index", required_argument, NULL, 'I'}, + {"base-path", required_argument, NULL, 'b'}, + {"ipv6", no_argument, NULL, '6'}, + {"ssl", no_argument, NULL, 'S'}, + {"ssl-cert", required_argument, NULL, 'C'}, + {"ssl-key", required_argument, NULL, 'K'}, + {"ssl-ca", required_argument, NULL, 'A'}, + {"url-arg", no_argument, NULL, 'a'}, + {"readonly", no_argument, NULL, 'R'}, + {"terminal-type", required_argument, NULL, 'T'}, + {"client-option", required_argument, NULL, 't'}, + {"check-origin", no_argument, NULL, 'O'}, + {"max-clients", required_argument, NULL, 'm'}, + {"once", no_argument, NULL, 'o'}, + {"browser", no_argument, NULL, 'B'}, + {"debug", required_argument, NULL, 'd'}, + {"version", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, 0, 0}}; static const char *opt_string = "p:i:c:u:g:s:I:b:6aSC:K:A:Rt:T:Om:oBd:vh"; -static void -print_help() { - fprintf(stderr, "ttyd is a tool for sharing terminal over the web\n\n" - "USAGE:\n" - " ttyd [options] []\n\n" - "VERSION:\n" - " %s\n\n" - "OPTIONS:\n" - " -p, --port Port to listen (default: 7681, use `0` for random port)\n" - " -i, --interface Network interface to bind (eg: eth0), or UNIX domain socket path (eg: /var/run/ttyd.sock)\n" - " -c, --credential Credential for Basic Authentication (format: username:password)\n" - " -u, --uid User id to run with\n" - " -g, --gid Group id to run with\n" - " -s, --signal Signal to send to the command when exit it (default: 1, SIGHUP)\n" - " -a, --url-arg Allow client to send command line arguments in URL (eg: http://localhost:7681?arg=foo&arg=bar)\n" - " -R, --readonly Do not allow clients to write to the TTY\n" - " -t, --client-option Send option to client (format: key=value), repeat to add more options\n" - " -T, --terminal-type Terminal type to report, default: xterm-256color\n" - " -O, --check-origin Do not allow websocket connection from different origin\n" - " -m, --max-clients Maximum clients to support (default: 0, no limit)\n" - " -o, --once Accept only one client and exit on disconnection\n" - " -B, --browser Open terminal with the default system browser\n" - " -I, --index Custom index.html path\n" - " -b, --base-path Expected base path for requests coming from a reverse proxy (eg: /mounted/here)\n" +static void print_help() { + // clang-format off + fprintf(stderr, "ttyd is a tool for sharing terminal over the web\n\n" + "USAGE:\n" + " ttyd [options] []\n\n" + "VERSION:\n" + " %s\n\n" + "OPTIONS:\n" + " -p, --port Port to listen (default: 7681, use `0` for random port)\n" + " -i, --interface Network interface to bind (eg: eth0), or UNIX domain socket path (eg: /var/run/ttyd.sock)\n" + " -c, --credential Credential for Basic Authentication (format: username:password)\n" + " -u, --uid User id to run with\n" + " -g, --gid Group id to run with\n" + " -s, --signal Signal to send to the command when exit it (default: 1, SIGHUP)\n" + " -a, --url-arg Allow client to send command line arguments in URL (eg: http://localhost:7681?arg=foo&arg=bar)\n" + " -R, --readonly Do not allow clients to write to the TTY\n" + " -t, --client-option Send option to client (format: key=value), repeat to add more options\n" + " -T, --terminal-type Terminal type to report, default: xterm-256color\n" + " -O, --check-origin Do not allow websocket connection from different origin\n" + " -m, --max-clients Maximum clients to support (default: 0, no limit)\n" + " -o, --once Accept only one client and exit on disconnection\n" + " -B, --browser Open terminal with the default system browser\n" + " -I, --index Custom index.html path\n" + " -b, --base-path Expected base path for requests coming from a reverse proxy (eg: /mounted/here)\n" #ifdef LWS_WITH_IPV6 - " -6, --ipv6 Enable IPv6 support\n" + " -6, --ipv6 Enable IPv6 support\n" #endif - " -S, --ssl Enable SSL\n" - " -C, --ssl-cert SSL certificate file path\n" - " -K, --ssl-key SSL key file path\n" - " -A, --ssl-ca SSL CA file path for client certificate verification\n" - " -d, --debug Set log level (default: 7)\n" - " -v, --version Print the version and exit\n" - " -h, --help Print this text and exit\n\n" - "Visit https://github.com/tsl0922/ttyd to get more information and report bugs.\n", - TTYD_VERSION - ); + " -S, --ssl Enable SSL\n" + " -C, --ssl-cert SSL certificate file path\n" + " -K, --ssl-key SSL key file path\n" + " -A, --ssl-ca SSL CA file path for client certificate verification\n" + " -d, --debug Set log level (default: 7)\n" + " -v, --version Print the version and exit\n" + " -h, --help Print this text and exit\n\n" + "Visit https://github.com/tsl0922/ttyd to get more information and report bugs.\n", + TTYD_VERSION + ); + // clang-format on } -static struct server * -server_new(int argc, char **argv, int start) { - struct server *ts; - size_t cmd_len = 0; - - ts = xmalloc(sizeof(struct server)); - - memset(ts, 0, sizeof(struct server)); - LIST_INIT(&ts->procs); - ts->client_count = 0; - ts->sig_code = SIGHUP; - sprintf(ts->terminal_type, "%s", "xterm-256color"); - get_sig_name(ts->sig_code, ts->sig_name, sizeof(ts->sig_name)); - if (start == argc) - return ts; - - int cmd_argc = argc - start; - char **cmd_argv = &argv[start]; - ts->argv = xmalloc(sizeof(char *) * (cmd_argc + 1)); - for (int i = 0; i < cmd_argc; i++) { - ts->argv[i] = strdup(cmd_argv[i]); - cmd_len += strlen(ts->argv[i]); - if (i != cmd_argc - 1) { - cmd_len++; // for space - } +static struct server *server_new(int argc, char **argv, int start) { + struct server *ts; + size_t cmd_len = 0; + + ts = xmalloc(sizeof(struct server)); + + memset(ts, 0, sizeof(struct server)); + LIST_INIT(&ts->procs); + ts->client_count = 0; + ts->sig_code = SIGHUP; + sprintf(ts->terminal_type, "%s", "xterm-256color"); + get_sig_name(ts->sig_code, ts->sig_name, sizeof(ts->sig_name)); + if (start == argc) return ts; + + int cmd_argc = argc - start; + char **cmd_argv = &argv[start]; + ts->argv = xmalloc(sizeof(char *) * (cmd_argc + 1)); + for (int i = 0; i < cmd_argc; i++) { + ts->argv[i] = strdup(cmd_argv[i]); + cmd_len += strlen(ts->argv[i]); + if (i != cmd_argc - 1) { + cmd_len++; // for space } - ts->argv[cmd_argc] = NULL; - ts->argc = cmd_argc; - - ts->command = xmalloc(cmd_len + 1); - char *ptr = ts->command; - for (int i = 0; i < cmd_argc; i++) { - ptr = stpcpy(ptr, ts->argv[i]); - if (i != cmd_argc - 1) { - *ptr++ = ' '; - } + } + ts->argv[cmd_argc] = NULL; + ts->argc = cmd_argc; + + ts->command = xmalloc(cmd_len + 1); + char *ptr = ts->command; + for (int i = 0; i < cmd_argc; i++) { + ptr = stpcpy(ptr, ts->argv[i]); + if (i != cmd_argc - 1) { + *ptr++ = ' '; } - *ptr = '\0'; // null terminator + } + *ptr = '\0'; // null terminator - ts->loop = xmalloc(sizeof *ts->loop); - uv_loop_init(ts->loop); - uv_signal_init(ts->loop, &ts->watcher); - ts->watcher.data = &ts->procs; + ts->loop = xmalloc(sizeof *ts->loop); + uv_loop_init(ts->loop); + uv_signal_init(ts->loop, &ts->watcher); + ts->watcher.data = &ts->procs; - return ts; + return ts; } -static void -server_free(struct server *ts) { - if (ts == NULL) - return; - if (ts->credential != NULL) - free(ts->credential); - if (ts->index != NULL) - free(ts->index); - free(ts->command); - free(ts->prefs_json); - int i = 0; - do { - free(ts->argv[i++]); - } while (ts->argv[i] != NULL); - free(ts->argv); - if (strlen(ts->socket_path) > 0) { - struct stat st; - if (!stat(ts->socket_path, &st)) { - unlink(ts->socket_path); - } +static void server_free(struct server *ts) { + if (ts == NULL) return; + if (ts->credential != NULL) free(ts->credential); + if (ts->index != NULL) free(ts->index); + free(ts->command); + free(ts->prefs_json); + int i = 0; + do { + free(ts->argv[i++]); + } while (ts->argv[i] != NULL); + free(ts->argv); + if (strlen(ts->socket_path) > 0) { + struct stat st; + if (!stat(ts->socket_path, &st)) { + unlink(ts->socket_path); } - uv_signal_stop(&ts->watcher); - uv_loop_close(ts->loop); - free(ts->loop); - free(ts); + } + uv_signal_stop(&ts->watcher); + uv_loop_close(ts->loop); + free(ts->loop); + free(ts); } -static void -signal_cb(uv_signal_t *watcher, int signum) { - char sig_name[20]; - - switch (watcher->signum) { - case SIGINT: - case SIGTERM: - get_sig_name(watcher->signum, sig_name, sizeof(sig_name)); - lwsl_notice("received signal: %s (%d), exiting...\n", sig_name, watcher->signum); - break; - default: - signal(SIGABRT, SIG_DFL); - abort(); - } - - if (force_exit) - exit(EXIT_FAILURE); - force_exit = true; - lws_cancel_service(context); +static void signal_cb(uv_signal_t *watcher, int signum) { + char sig_name[20]; + + switch (watcher->signum) { + case SIGINT: + case SIGTERM: + get_sig_name(watcher->signum, sig_name, sizeof(sig_name)); + lwsl_notice("received signal: %s (%d), exiting...\n", sig_name, + watcher->signum); + break; + default: + signal(SIGABRT, SIG_DFL); + abort(); + } + + if (force_exit) exit(EXIT_FAILURE); + force_exit = true; + lws_cancel_service(context); #if LWS_LIBRARY_VERSION_MAJOR >= 3 - uv_stop(server->loop); - lwsl_notice("send ^C to force exit.\n"); + uv_stop(server->loop); + lwsl_notice("send ^C to force exit.\n"); #else - lws_libuv_stop(context); - exit(EXIT_SUCCESS); + lws_libuv_stop(context); + exit(EXIT_SUCCESS); #endif } -static int -calc_command_start(int argc, char **argv) { - // make a copy of argc and argv - int argc_copy = argc; - char **argv_copy = xmalloc(sizeof(char *) * argc); +static int calc_command_start(int argc, char **argv) { + // make a copy of argc and argv + int argc_copy = argc; + char **argv_copy = xmalloc(sizeof(char *) * argc); + for (int i = 0; i < argc; i++) { + argv_copy[i] = strdup(argv[i]); + } + + // do not print error message for invalid option + opterr = 0; + while (getopt_long(argc_copy, argv_copy, opt_string, options, NULL) != -1) + ; + + int start = argc; + if (optind < argc) { + char *command = argv_copy[optind]; for (int i = 0; i < argc; i++) { - argv_copy[i] = strdup(argv[i]); - } - - // do not print error message for invalid option - opterr = 0; - while (getopt_long(argc_copy, argv_copy, opt_string, options, NULL) != -1) - ; - - int start = argc; - if (optind < argc) { - char *command = argv_copy[optind]; - for (int i = 0; i < argc; i++) { - if (strcmp(argv[i], command) == 0) { - start = i; - break; - } - } + if (strcmp(argv[i], command) == 0) { + start = i; + break; + } } + } - // free argv copy - for (int i = 0; i < argc; i++) { - free(argv_copy[i]); - } - free(argv_copy); + // free argv copy + for (int i = 0; i < argc; i++) { + free(argv_copy[i]); + } + free(argv_copy); - // reset for next use - opterr = 1; - optind = 0; + // reset for next use + opterr = 1; + optind = 0; - return start; + return start; } -int -main(int argc, char **argv) { - if (argc == 1) { - print_help(); - return 0; - } - - int start = calc_command_start(argc, argv); - server = server_new(argc, argv, start); - - struct lws_context_creation_info info; - memset(&info, 0, sizeof(info)); - info.port = 7681; - info.iface = NULL; - info.protocols = protocols; - info.ssl_cert_filepath = NULL; - info.ssl_private_key_filepath = NULL; - info.gid = -1; - info.uid = -1; - info.max_http_header_pool = 16; - info.options = LWS_SERVER_OPTION_LIBUV | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_DISABLE_IPV6; +int main(int argc, char **argv) { + if (argc == 1) { + print_help(); + return 0; + } + + int start = calc_command_start(argc, argv); + server = server_new(argc, argv, start); + + struct lws_context_creation_info info; + memset(&info, 0, sizeof(info)); + info.port = 7681; + info.iface = NULL; + info.protocols = protocols; + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + info.gid = -1; + info.uid = -1; + info.max_http_header_pool = 16; + info.options = LWS_SERVER_OPTION_LIBUV | LWS_SERVER_OPTION_VALIDATE_UTF8 | + LWS_SERVER_OPTION_DISABLE_IPV6; #ifndef LWS_WITHOUT_EXTENSIONS - info.extensions = extensions; + info.extensions = extensions; #endif - info.max_http_header_data = 20480; - - int debug_level = LLL_ERR | LLL_WARN | LLL_NOTICE; - char iface[128] = ""; - bool browser = false; - bool ssl = false; - char cert_path[1024] = ""; - char key_path[1024] = ""; - char ca_path[1024] = ""; - - struct json_object *client_prefs = json_object_new_object(); - - // parse command line options - int c; - while ((c = getopt_long(start, argv, opt_string, options, NULL)) != -1) { - switch (c) { - case 'h': - print_help(); - return 0; - case 'v': - printf("ttyd version %s\n", TTYD_VERSION); - return 0; - case 'd': - debug_level = atoi(optarg); - break; - case 'a': - server->url_arg = true; - break; - case 'R': - server->readonly = true; - break; - case 'O': - server->check_origin = true; - break; - case 'm': - server->max_clients = atoi(optarg); - break; - case 'o': - server->once = true; - break; - case 'B': - browser = true; - break; - case 'p': - info.port = atoi(optarg); - if (info.port < 0) { - fprintf(stderr, "ttyd: invalid port: %s\n", optarg); - return -1; - } - break; - case 'i': - strncpy(iface, optarg, sizeof(iface) - 1); - iface[sizeof(iface) - 1] = '\0'; - break; - case 'c': - if (strchr(optarg, ':') == NULL) { - fprintf(stderr, "ttyd: invalid credential, format: username:password\n"); - return -1; - } - server->credential = base64_encode((const unsigned char *) optarg, strlen(optarg)); - break; - case 'u': - info.uid = atoi(optarg); - break; - case 'g': - info.gid = atoi(optarg); - break; - case 's': { - int sig = get_sig(optarg); - if (sig > 0) { - server->sig_code = sig; - get_sig_name(sig, server->sig_name, sizeof(server->sig_name)); - } else { - fprintf(stderr, "ttyd: invalid signal: %s\n", optarg); - return -1; - } - } - break; - case 'I': - if (!strncmp(optarg, "~/", 2)) { - const char *home = getenv("HOME"); - server->index = malloc(strlen(home) + strlen(optarg) - 1); - sprintf(server->index, "%s%s", home, optarg + 1); - } else { - server->index = strdup(optarg); - } - struct stat st; - if (stat(server->index, &st) == -1) { - fprintf(stderr, "Can not stat index.html: %s, error: %s\n", server->index, strerror(errno)); - return -1; - } - if (S_ISDIR(st.st_mode)) { - fprintf(stderr, "Invalid index.html path: %s, is it a dir?\n", server->index); - return -1; - } - break; - case 'b': { - char path[128]; - strncpy(path, optarg, 128); - size_t len = strlen(path); - #define sc(f) \ - strncpy(path + len, endpoints.f, 128 - len); \ - endpoints.f = strdup(path); - sc(ws) sc(index) sc(token) - #undef sc - } - break; - case '6': - info.options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6); - break; - case 'S': - ssl = true; - break; - case 'C': - strncpy(cert_path, optarg, sizeof(cert_path) - 1); - cert_path[sizeof(cert_path) - 1] = '\0'; - break; - case 'K': - strncpy(key_path, optarg, sizeof(key_path) - 1); - key_path[sizeof(key_path) - 1] = '\0'; - break; - case 'A': - strncpy(ca_path, optarg, sizeof(ca_path) - 1); - ca_path[sizeof(ca_path) - 1] = '\0'; - break; - case 'T': - strncpy(server->terminal_type, optarg, sizeof(server->terminal_type) - 1); - server->terminal_type[sizeof(server->terminal_type) - 1] = '\0'; - break; - case '?': - break; - case 't': - optind--; - for (; optind < start && *argv[optind] != '-'; optind++) { - char *option = strdup(optarg); - char *key = strsep(&option, "="); - if (key == NULL) { - fprintf(stderr, "ttyd: invalid client option: %s, format: key=value\n", optarg); - return -1; - } - char *value = strsep(&option, "="); - if (value == NULL) { - fprintf(stderr, "ttyd: invalid client option: %s, format: key=value\n", optarg); - return -1; - } - free(option); - struct json_object *obj = json_tokener_parse(value); - json_object_object_add(client_prefs, key, obj != NULL ? obj : json_object_new_string(value)); - } - break; - default: - print_help(); - return -1; + info.max_http_header_data = 20480; + + int debug_level = LLL_ERR | LLL_WARN | LLL_NOTICE; + char iface[128] = ""; + bool browser = false; + bool ssl = false; + char cert_path[1024] = ""; + char key_path[1024] = ""; + char ca_path[1024] = ""; + + struct json_object *client_prefs = json_object_new_object(); + + // parse command line options + int c; + while ((c = getopt_long(start, argv, opt_string, options, NULL)) != -1) { + switch (c) { + case 'h': + print_help(); + return 0; + case 'v': + printf("ttyd version %s\n", TTYD_VERSION); + return 0; + case 'd': + debug_level = atoi(optarg); + break; + case 'a': + server->url_arg = true; + break; + case 'R': + server->readonly = true; + break; + case 'O': + server->check_origin = true; + break; + case 'm': + server->max_clients = atoi(optarg); + break; + case 'o': + server->once = true; + break; + case 'B': + browser = true; + break; + case 'p': + info.port = atoi(optarg); + if (info.port < 0) { + fprintf(stderr, "ttyd: invalid port: %s\n", optarg); + return -1; } - } - server->prefs_json = strdup(json_object_to_json_string(client_prefs)); - json_object_put(client_prefs); - - if (server->command == NULL || strlen(server->command) == 0) { - fprintf(stderr, "ttyd: missing start command\n"); + break; + case 'i': + strncpy(iface, optarg, sizeof(iface) - 1); + iface[sizeof(iface) - 1] = '\0'; + break; + case 'c': + if (strchr(optarg, ':') == NULL) { + fprintf(stderr, + "ttyd: invalid credential, format: username:password\n"); + return -1; + } + server->credential = + base64_encode((const unsigned char *)optarg, strlen(optarg)); + break; + case 'u': + info.uid = atoi(optarg); + break; + case 'g': + info.gid = atoi(optarg); + break; + case 's': { + int sig = get_sig(optarg); + if (sig > 0) { + server->sig_code = sig; + get_sig_name(sig, server->sig_name, sizeof(server->sig_name)); + } else { + fprintf(stderr, "ttyd: invalid signal: %s\n", optarg); + return -1; + } + } break; + case 'I': + if (!strncmp(optarg, "~/", 2)) { + const char *home = getenv("HOME"); + server->index = malloc(strlen(home) + strlen(optarg) - 1); + sprintf(server->index, "%s%s", home, optarg + 1); + } else { + server->index = strdup(optarg); + } + struct stat st; + if (stat(server->index, &st) == -1) { + fprintf(stderr, "Can not stat index.html: %s, error: %s\n", + server->index, strerror(errno)); + return -1; + } + if (S_ISDIR(st.st_mode)) { + fprintf(stderr, "Invalid index.html path: %s, is it a dir?\n", + server->index); + return -1; + } + break; + case 'b': { + char path[128]; + strncpy(path, optarg, 128); + size_t len = strlen(path); +#define sc(f) \ + strncpy(path + len, endpoints.f, 128 - len); \ + endpoints.f = strdup(path); + sc(ws) sc(index) sc(token) +#undef sc + } break; + case '6': + info.options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6); + break; + case 'S': + ssl = true; + break; + case 'C': + strncpy(cert_path, optarg, sizeof(cert_path) - 1); + cert_path[sizeof(cert_path) - 1] = '\0'; + break; + case 'K': + strncpy(key_path, optarg, sizeof(key_path) - 1); + key_path[sizeof(key_path) - 1] = '\0'; + break; + case 'A': + strncpy(ca_path, optarg, sizeof(ca_path) - 1); + ca_path[sizeof(ca_path) - 1] = '\0'; + break; + case 'T': + strncpy(server->terminal_type, optarg, + sizeof(server->terminal_type) - 1); + server->terminal_type[sizeof(server->terminal_type) - 1] = '\0'; + break; + case '?': + break; + case 't': + optind--; + for (; optind < start && *argv[optind] != '-'; optind++) { + char *option = strdup(optarg); + char *key = strsep(&option, "="); + if (key == NULL) { + fprintf(stderr, + "ttyd: invalid client option: %s, format: key=value\n", + optarg); + return -1; + } + char *value = strsep(&option, "="); + if (value == NULL) { + fprintf(stderr, + "ttyd: invalid client option: %s, format: key=value\n", + optarg); + return -1; + } + free(option); + struct json_object *obj = json_tokener_parse(value); + json_object_object_add( + client_prefs, key, + obj != NULL ? obj : json_object_new_string(value)); + } + break; + default: + print_help(); return -1; } + } + server->prefs_json = strdup(json_object_to_json_string(client_prefs)); + json_object_put(client_prefs); - lws_set_log_level(debug_level, NULL); + if (server->command == NULL || strlen(server->command) == 0) { + fprintf(stderr, "ttyd: missing start command\n"); + return -1; + } + + lws_set_log_level(debug_level, NULL); #if LWS_LIBRARY_VERSION_MAJOR >= 2 - char server_hdr[128] = ""; - sprintf(server_hdr, "ttyd/%s (libwebsockets/%s)", TTYD_VERSION, LWS_LIBRARY_VERSION); - info.server_string = server_hdr; + char server_hdr[128] = ""; + sprintf(server_hdr, "ttyd/%s (libwebsockets/%s)", TTYD_VERSION, + LWS_LIBRARY_VERSION); + info.server_string = server_hdr; #if LWS_LIBRARY_VERSION_MINOR >= 1 - info.ws_ping_pong_interval = 5; + info.ws_ping_pong_interval = 5; #endif #endif - if (strlen(iface) > 0) { - info.iface = iface; - if (endswith(info.iface, ".sock") || endswith(info.iface, ".socket")) { + if (strlen(iface) > 0) { + info.iface = iface; + if (endswith(info.iface, ".sock") || endswith(info.iface, ".socket")) { #if defined(LWS_USE_UNIX_SOCK) || defined(LWS_WITH_UNIX_SOCK) - info.options |= LWS_SERVER_OPTION_UNIX_SOCK; - strncpy(server->socket_path, info.iface, sizeof(server->socket_path) - 1); + info.options |= LWS_SERVER_OPTION_UNIX_SOCK; + strncpy(server->socket_path, info.iface, sizeof(server->socket_path) - 1); #else - fprintf(stderr, "libwebsockets is not compiled with UNIX domain socket support"); - return -1; + fprintf(stderr, + "libwebsockets is not compiled with UNIX domain socket support"); + return -1; #endif - } } - - if (ssl) { - info.ssl_cert_filepath = cert_path; - info.ssl_private_key_filepath = key_path; - info.ssl_ca_filepath = ca_path; - if (strlen(info.ssl_ca_filepath) > 0) - info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT; + } + + if (ssl) { + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + info.ssl_ca_filepath = ca_path; + if (strlen(info.ssl_ca_filepath) > 0) + info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT; #if LWS_LIBRARY_VERSION_MAJOR >= 2 - info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS; + info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS; #endif - } - - lwsl_notice("ttyd %s (libwebsockets %s)\n", TTYD_VERSION, LWS_LIBRARY_VERSION); - lwsl_notice("tty configuration:\n"); - if (server->credential != NULL) - lwsl_notice(" credential: %s\n", server->credential); - lwsl_notice(" start command: %s\n", server->command); - lwsl_notice(" close signal: %s (%d)\n", server->sig_name, server->sig_code); - lwsl_notice(" terminal type: %s\n", server->terminal_type); - if (server->check_origin) - lwsl_notice(" check origin: true\n"); - if (server->url_arg) - lwsl_notice(" allow url arg: true\n"); - if (server->readonly) - lwsl_notice(" readonly: true\n"); - if (server->max_clients > 0) - lwsl_notice(" max clients: %d\n", server->max_clients); - if (server->once) - lwsl_notice(" once: true\n"); - if (server->index != NULL) { - lwsl_notice(" custom index.html: %s\n", server->index); - } + } + + lwsl_notice("ttyd %s (libwebsockets %s)\n", TTYD_VERSION, + LWS_LIBRARY_VERSION); + lwsl_notice("tty configuration:\n"); + if (server->credential != NULL) + lwsl_notice(" credential: %s\n", server->credential); + lwsl_notice(" start command: %s\n", server->command); + lwsl_notice(" close signal: %s (%d)\n", server->sig_name, server->sig_code); + lwsl_notice(" terminal type: %s\n", server->terminal_type); + if (server->check_origin) lwsl_notice(" check origin: true\n"); + if (server->url_arg) lwsl_notice(" allow url arg: true\n"); + if (server->readonly) lwsl_notice(" readonly: true\n"); + if (server->max_clients > 0) + lwsl_notice(" max clients: %d\n", server->max_clients); + if (server->once) lwsl_notice(" once: true\n"); + if (server->index != NULL) { + lwsl_notice(" custom index.html: %s\n", server->index); + } #if LWS_LIBRARY_VERSION_MAJOR >= 3 - void *foreign_loops[1]; - foreign_loops[0] = server->loop; - info.foreign_loops = foreign_loops; - info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + void *foreign_loops[1]; + foreign_loops[0] = server->loop; + info.foreign_loops = foreign_loops; + info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; #endif - context = lws_create_context(&info); - if (context == NULL) { - lwsl_err("libwebsockets context creation failed\n"); - return 1; - } + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsockets context creation failed\n"); + return 1; + } #if LWS_LIBRARY_VERSION_MAJOR >= 3 - struct lws_vhost *vhost = lws_create_vhost(context, &info); - if (vhost == NULL) { - lwsl_err("libwebsockets vhost creation failed\n"); - return 1; - } - int port = lws_get_vhost_listen_port(vhost); + struct lws_vhost *vhost = lws_create_vhost(context, &info); + if (vhost == NULL) { + lwsl_err("libwebsockets vhost creation failed\n"); + return 1; + } + int port = lws_get_vhost_listen_port(vhost); #else - int port = info.port; + int port = info.port; #endif - lwsl_notice(" Listening on port: %d\n", port); + lwsl_notice(" Listening on port: %d\n", port); - if (browser) { - char url[30]; - sprintf(url, "%s://localhost:%d", ssl ? "https" : "http", port); - open_uri(url); - } + if (browser) { + char url[30]; + sprintf(url, "%s://localhost:%d", ssl ? "https" : "http", port); + open_uri(url); + } #if LWS_LIBRARY_VERSION_MAJOR >= 3 - int sig_nums[] = {SIGINT, SIGTERM}; - int ns = sizeof(sig_nums)/sizeof(sig_nums[0]); - uv_signal_t signals[ns]; - for (int i = 0; i < ns; i++) { - uv_signal_init(server->loop, &signals[i]); - uv_signal_start(&signals[i], signal_cb, sig_nums[i]); - } - - lws_service(context, 0); - - for (int i = 0; i < ns; i++) { - uv_signal_stop(&signals[i]); - } + int sig_nums[] = {SIGINT, SIGTERM}; + int ns = sizeof(sig_nums) / sizeof(sig_nums[0]); + uv_signal_t signals[ns]; + for (int i = 0; i < ns; i++) { + uv_signal_init(server->loop, &signals[i]); + uv_signal_start(&signals[i], signal_cb, sig_nums[i]); + } + + lws_service(context, 0); + + for (int i = 0; i < ns; i++) { + uv_signal_stop(&signals[i]); + } #else #if LWS_LIBRARY_VERSION_MAJOR < 2 - lws_uv_initloop(context, server->loop, signal_cb, 0); + lws_uv_initloop(context, server->loop, signal_cb, 0); #else - lws_uv_sigint_cfg(context, 1, signal_cb); - lws_uv_initloop(context, server->loop, 0); + lws_uv_sigint_cfg(context, 1, signal_cb); + lws_uv_initloop(context, server->loop, 0); #endif - lws_libuv_run(context, 0); + lws_libuv_run(context, 0); #endif - lws_context_destroy(context); + lws_context_destroy(context); - // cleanup - server_free(server); + // cleanup + server_free(server); - return 0; + return 0; } diff --git a/src/server.h b/src/server.h index 7fed37d..cd6f49d 100644 --- a/src/server.h +++ b/src/server.h @@ -15,9 +15,9 @@ // url paths struct endpoints { - char *ws; - char *index; - char *token; + char *ws; + char *index; + char *token; }; extern volatile bool force_exit; @@ -25,74 +25,66 @@ extern struct lws_context *context; extern struct server *server; extern struct endpoints endpoints; -typedef enum { - STATE_INIT, STATE_KILL, STATE_EXIT -} proc_state; +typedef enum { STATE_INIT, STATE_KILL, STATE_EXIT } proc_state; struct pss_http { - char path[128]; - char *buffer; - char *ptr; - size_t len; + char path[128]; + char *buffer; + char *ptr; + size_t len; }; struct pty_proc { - char **args; - int argc; + char **args; + int argc; - pid_t pid; - int status; - proc_state state; + pid_t pid; + int status; + proc_state state; - int pty; - char *pty_buffer; - ssize_t pty_len; + int pty; + char *pty_buffer; + ssize_t pty_len; - uv_pipe_t pipe; + uv_pipe_t pipe; - LIST_ENTRY(pty_proc) entry; + LIST_ENTRY(pty_proc) entry; }; struct pss_tty { - bool initialized; - int initial_cmd_index; - bool authenticated; - char address[50]; + bool initialized; + int initial_cmd_index; + bool authenticated; + char address[50]; - struct lws *wsi; - char *buffer; - size_t len; + struct lws *wsi; + char *buffer; + size_t len; - struct pty_proc *proc; + struct pty_proc *proc; }; struct server { - int client_count; // client count - char *prefs_json; // client preferences - char *credential; // encoded basic auth credential - char *index; // custom index.html - char *command; // full command line - char **argv; // command with arguments - int argc; // command + arguments count - int sig_code; // close signal - char sig_name[20]; // human readable signal string - bool url_arg; // allow client to send cli arguments in URL - bool readonly; // whether not allow clients to write to the TTY - bool check_origin; // whether allow websocket connection from different origin - int max_clients; // maximum clients to support - bool once; // whether accept only one client and exit on disconnection - char socket_path[255]; // UNIX domain socket path - char terminal_type[30]; // terminal type to report - - uv_loop_t *loop; // the libuv event loop - uv_signal_t watcher; // SIGCHLD watcher - - LIST_HEAD(proc, pty_proc) procs; // started process list + int client_count; // client count + char *prefs_json; // client preferences + char *credential; // encoded basic auth credential + char *index; // custom index.html + char *command; // full command line + char **argv; // command with arguments + int argc; // command + arguments count + int sig_code; // close signal + char sig_name[20]; // human readable signal string + bool url_arg; // allow client to send cli arguments in URL + bool readonly; // whether not allow clients to write to the TTY + bool + check_origin; // whether allow websocket connection from different origin + int max_clients; // maximum clients to support + bool once; // whether accept only one client and exit on disconnection + char socket_path[255]; // UNIX domain socket path + char terminal_type[30]; // terminal type to report + + uv_loop_t *loop; // the libuv event loop + uv_signal_t watcher; // SIGCHLD watcher + + LIST_HEAD(proc, pty_proc) procs; // started process list }; - -extern int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); - -extern int -callback_tty(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); - diff --git a/src/terminal.c b/src/terminal.c index 3992ffb..140573f 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -1,10 +1,10 @@ +#include +#include #include #include #include -#include -#include -#include #include +#include #if defined(__OpenBSD__) || defined(__APPLE__) #include @@ -16,40 +16,39 @@ #include "utils.h" -pid_t -pty_fork(int *pty, const char *file, char *const argv[], const char *term) { - pid_t pid = forkpty(pty, NULL, NULL, NULL); - - if (pid < 0) { - return pid; - } else if (pid == 0) { - setenv("TERM", term, true); - int ret = execvp(file, argv); - if (ret < 0) { - perror("execvp failed\n"); - _exit(-errno); - } - } +pid_t pty_fork(int *pty, const char *file, char *const argv[], + const char *term) { + pid_t pid = forkpty(pty, NULL, NULL, NULL); - // set the file descriptor non blocking - int flags = fcntl(*pty, F_GETFL); - if (flags != -1) { - fcntl(*pty, F_SETFD, flags | O_NONBLOCK); + if (pid < 0) { + return pid; + } else if (pid == 0) { + setenv("TERM", term, true); + int ret = execvp(file, argv); + if (ret < 0) { + perror("execvp failed\n"); + _exit(-errno); } - // set the file descriptor close-on-exec - fd_set_cloexec(*pty); + } - return pid; + // set the file descriptor non blocking + int flags = fcntl(*pty, F_GETFL); + if (flags != -1) { + fcntl(*pty, F_SETFD, flags | O_NONBLOCK); + } + // set the file descriptor close-on-exec + fd_set_cloexec(*pty); + + return pid; } -int -pty_resize(pid_t pty, int cols, int rows) { - struct winsize size; +int pty_resize(pid_t pty, int cols, int rows) { + struct winsize size; - size.ws_col = (unsigned short) cols; - size.ws_row = (unsigned short) rows; - size.ws_xpixel = 0; - size.ws_ypixel = 0; + size.ws_col = (unsigned short)cols; + size.ws_row = (unsigned short)rows; + size.ws_xpixel = 0; + size.ws_ypixel = 0; - return ioctl(pty, TIOCSWINSZ, &size); + return ioctl(pty, TIOCSWINSZ, &size); } diff --git a/src/terminal.h b/src/terminal.h index 2dc9c02..63253e9 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -1,10 +1,8 @@ #ifndef TTYD_TERMINAL_H #define TTYD_TERMINAL_H -int -pty_fork(int *pty, const char *file, char *const argv[], const char *term); +int pty_fork(int *pty, const char *file, char *const argv[], const char *term); -int -pty_resize(int pty, int cols, int rows); +int pty_resize(int pty, int cols, int rows); -#endif //TTYD_TERMINAL_H +#endif // TTYD_TERMINAL_H diff --git a/src/utils.c b/src/utils.c index 4853456..0523451 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,138 +1,124 @@ +#include +#include +#include +#include #include #include -#include -#include #include -#include -#include #if defined(__linux__) && !defined(__ANDROID__) // https://github.com/karelzak/util-linux/blob/master/misc-utils/kill.c const char *sys_signame[NSIG] = { - "zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "UNUSED", - "FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", - "STKFLT","CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", - "XCPU", "XFSZ", "VTALRM","PROF", "WINCH", "IO", "PWR", "SYS", NULL -}; + "zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "UNUSED", "FPE", + "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", + "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU", "XFSZ", "VTALRM", + "PROF", "WINCH", "IO", "PWR", "SYS", NULL}; #endif #if defined(_WIN32) || defined(__CYGWIN__) -#include #include +#include // https://github.com/mirror/newlib-cygwin/blob/master/winsup/cygwin/strsig.cc #ifndef NSIG #define NSIG 33 #endif const char *sys_signame[NSIG] = { - "zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT", - "FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", - "URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO", - "XCPU", "XFSZ", "VTALRM","PROF", "WINCH", "PWR", "USR1", "USR2", NULL -}; + "zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT", "FPE", + "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", + "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", "XFSZ", "VTALRM", + "PROF", "WINCH", "PWR", "USR1", "USR2", NULL}; #endif -void * -xmalloc(size_t size) { - if (size == 0) - return NULL; - void *p = malloc(size); - if (!p) - abort(); - return p; +void *xmalloc(size_t size) { + if (size == 0) return NULL; + void *p = malloc(size); + if (!p) abort(); + return p; } -void * -xrealloc(void *p, size_t size) { - if ((size == 0) && (p == NULL)) - return NULL; - p = realloc(p, size); - if (!p) - abort(); - return p; +void *xrealloc(void *p, size_t size) { + if ((size == 0) && (p == NULL)) return NULL; + p = realloc(p, size); + if (!p) abort(); + return p; } -char * -uppercase(char *str) { - int i = 0; - do { - str[i] = (char) toupper(str[i]); - } while (str[i++] != '\0'); - return str; +char *uppercase(char *str) { + int i = 0; + do { + str[i] = (char)toupper(str[i]); + } while (str[i++] != '\0'); + return str; } -bool -endswith(const char *str, const char *suffix) { - size_t str_len = strlen(str); - size_t suffix_len = strlen(suffix); - return str_len > suffix_len && !strcmp(str + (str_len - suffix_len), suffix); +bool endswith(const char *str, const char *suffix) { + size_t str_len = strlen(str); + size_t suffix_len = strlen(suffix); + return str_len > suffix_len && !strcmp(str + (str_len - suffix_len), suffix); } -int -get_sig_name(int sig, char *buf, size_t len) { - int n = snprintf(buf, len, "SIG%s", sig < NSIG ? sys_signame[sig] : "unknown"); - uppercase(buf); - return n; +int get_sig_name(int sig, char *buf, size_t len) { + int n = + snprintf(buf, len, "SIG%s", sig < NSIG ? sys_signame[sig] : "unknown"); + uppercase(buf); + return n; } -int -get_sig(const char *sig_name) { - for (int sig = 1; sig < NSIG; sig++) { - const char *name = sys_signame[sig]; - if (name != NULL && (strcasecmp(name, sig_name) == 0 || strcasecmp(name, sig_name + 3) == 0)) - return sig; - } - return atoi(sig_name); +int get_sig(const char *sig_name) { + for (int sig = 1; sig < NSIG; sig++) { + const char *name = sys_signame[sig]; + if (name != NULL && (strcasecmp(name, sig_name) == 0 || + strcasecmp(name, sig_name + 3) == 0)) + return sig; + } + return atoi(sig_name); } -bool -fd_set_cloexec(const int fd) { - int flags = fcntl(fd, F_GETFD); - if (flags < 0) - return false; - return (flags & FD_CLOEXEC) == 0 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1; +bool fd_set_cloexec(const int fd) { + int flags = fcntl(fd, F_GETFD); + if (flags < 0) return false; + return (flags & FD_CLOEXEC) == 0 || + fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1; } -int -open_uri(char *uri) { +int open_uri(char *uri) { #ifdef __APPLE__ - char command[256]; - sprintf(command, "open %s > /dev/null 2>&1", uri); - return system(command); + char command[256]; + sprintf(command, "open %s > /dev/null 2>&1", uri); + return system(command); #elif defined(_WIN32) || defined(__CYGWIN__) - return ShellExecute(0, 0, uri, 0, 0 , SW_SHOW) > 32 ? 0 : 1; + return ShellExecute(0, 0, uri, 0, 0, SW_SHOW) > 32 ? 0 : 1; #else - // check if X server is running - if (system("xset -q > /dev/null 2>&1")) - return 1; - char command[256]; - sprintf(command, "xdg-open %s > /dev/null 2>&1", uri); - return system(command); + // check if X server is running + if (system("xset -q > /dev/null 2>&1")) return 1; + char command[256]; + sprintf(command, "xdg-open %s > /dev/null 2>&1", uri); + return system(command); #endif } // https://github.com/darkk/redsocks/blob/master/base64.c -char * -base64_encode(const unsigned char *buffer, size_t length) { - static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - char *ret, *dst; - unsigned i_bits = 0; - int i_shift = 0; - int bytes_remaining = (int) length; +char *base64_encode(const unsigned char *buffer, size_t length) { + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *ret, *dst; + unsigned i_bits = 0; + int i_shift = 0; + int bytes_remaining = (int)length; - ret = dst = xmalloc((size_t) (((length + 2) / 3 * 4) + 1)); - while (bytes_remaining) { - i_bits = (i_bits << 8) + *buffer++; - bytes_remaining--; - i_shift += 8; + ret = dst = xmalloc((size_t)(((length + 2) / 3 * 4) + 1)); + while (bytes_remaining) { + i_bits = (i_bits << 8) + *buffer++; + bytes_remaining--; + i_shift += 8; - do { - *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; - i_shift -= 6; - } while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0)); - } - while ((dst - ret) & 3) - *dst++ = '='; - *dst = '\0'; + do { + *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; + i_shift -= 6; + } while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0)); + } + while ((dst - ret) & 3) *dst++ = '='; + *dst = '\0'; - return ret; + return ret; } diff --git a/src/utils.h b/src/utils.h index a02d0c2..935bec4 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2,39 +2,30 @@ #define TTYD_UTIL_H // malloc with NULL check -void * -xmalloc(size_t size); +void *xmalloc(size_t size); // realloc with NULL check -void * -xrealloc(void *p, size_t size); +void *xrealloc(void *p, size_t size); // Convert a string to upper case -char * -uppercase(char *str); +char *uppercase(char *str); // Check whether str ends with suffix -bool -endswith(const char *str, const char *suffix); +bool endswith(const char *str, const char *suffix); // Get human readable signal string -int -get_sig_name(int sig, char *buf, size_t len); +int get_sig_name(int sig, char *buf, size_t len); // Get signal code from string like SIGHUP -int -get_sig(const char *sig_name); +int get_sig(const char *sig_name); // Set the given file descriptor close-on-exec -bool -fd_set_cloexec(const int fd); +bool fd_set_cloexec(const int fd); // Open uri with the default application of system -int -open_uri(char *uri); +int open_uri(char *uri); // Encode text to base64, the caller should free the returned string -char * -base64_encode(const unsigned char *buffer, size_t length); +char *base64_encode(const unsigned char *buffer, size_t length); -#endif //TTYD_UTIL_H +#endif // TTYD_UTIL_H -- 2.43.4