--- /dev/null
+BasedOnStyle: Google
+Language: Cpp
-#include <string.h>
#include <libwebsockets.h>
#include <openssl/ssl.h>
+#include <string.h>
#include <zlib.h>
-#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;
}
+#include <errno.h>
+#include <json.h>
+#include <libwebsockets.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <signal.h>
-#include <errno.h>
#include <sys/wait.h>
-#include <libwebsockets.h>
-#include <json.h>
-
#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;
}
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
+#include "server.h"
+
#include <errno.h>
#include <getopt.h>
+#include <json.h>
+#include <libwebsockets.h>
#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
-#include <libwebsockets.h>
-#include <json.h>
-
-#include "server.h"
#include "utils.h"
#ifndef TTYD_VERSION
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] <command> [<arguments...>]\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] <command> [<arguments...>]\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;
}
// url paths
struct endpoints {
- char *ws;
- char *index;
- char *token;
+ char *ws;
+ char *index;
+ char *token;
};
extern volatile bool force_exit;
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);
-
+#include <errno.h>
+#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <sys/ioctl.h>
+#include <unistd.h>
#if defined(__OpenBSD__) || defined(__APPLE__)
#include <util.h>
#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);
}
#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
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdbool.h>
-#include <ctype.h>
#include <string.h>
-#include <signal.h>
-#include <fcntl.h>
#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 <windows.h>
#include <shellapi.h>
+#include <windows.h>
// 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;
}
#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