src: reformat with clang-format

This commit is contained in:
Shuanglei Tao
2020-03-14 17:41:06 +08:00
parent e324cc3b91
commit 2fdb4c0c03
9 changed files with 1259 additions and 1309 deletions

View File

@@ -1,282 +1,269 @@
#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;
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;
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;
}
unsigned char buffer[1024 + LWS_PRE], *p, *end;
p = buffer + LWS_PRE;
end = p + sizeof(buffer) - LWS_PRE;
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);
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;
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);
pss->buffer = pss->ptr = body;
pss->len = n;
lws_callback_on_writable(wsi);
return AUTH_FAIL;
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;
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);
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;
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;
}
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;
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;
#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;
}
#endif
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_LIBRARY_VERSION_MAJOR < 2
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);
#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;
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:
break;
}
return 1;
}
return 0;
p = buffer + LWS_PRE;
end = p + sizeof(buffer) - LWS_PRE;
/* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */
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;
#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;
}
#endif
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_LIBRARY_VERSION_MAJOR < 2
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);
#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 */
try_to_reuse:
if (lws_http_transaction_completed(wsi))
return -1;
if (lws_http_transaction_completed(wsi)) return -1;
return 0;
return 0;
}

View File

@@ -1,468 +1,459 @@
#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 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;
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;
}
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);
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';
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;
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);
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;
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;
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);
close(proc->pty);
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]);
}
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 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);
}
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);
LIST_REMOVE(proc, entry);
if (proc->state == STATE_KILL) {
pty_proc_free(proc);
} else {
sprintf(buf, "%s:%d", address, port);
proc->state = STATE_EXIT;
}
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 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;
close(proc->pty);
uv_signal_start(&server->watcher, child_cb, SIGCHLD);
if (proc->pty_buffer != NULL) {
free(proc->pty_buffer);
proc->pty_buffer = NULL;
}
// ensure the lws socket fd close-on-exec
fd_set_cloexec(lws_get_socket_fd(pss->wsi));
for (int i = 0; i < proc->argc; i++) {
free(proc->args[i]);
}
// 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;
}
free(proc);
lwsl_notice("started process, pid: %d\n", proc->pid);
proc->pipe.data = pss;
uv_pipe_open(&proc->pipe, proc->pty);
lws_callback_on_writable(pss->wsi);
return 0;
}
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 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;
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
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 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
child_cb(uv_signal_t *handle, int signum) {
pid_t pid;
int stat;
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;
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;
}
}
}
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);
// 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));
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;
}
lwsl_notice("started process, pid: %d\n", proc->pid);
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;
proc->pipe.data = pss;
uv_pipe_open(&proc->pipe, proc->pty);
case LWS_CALLBACK_ESTABLISHED:
pss->initialized = false;
pss->initial_cmd_index = 0;
pss->authenticated = false;
pss->wsi = wsi;
pss->buffer = NULL;
lws_callback_on_writable(pss->wsi);
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);
return 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++;
}
}
}
static void
kill_process(struct pty_proc *proc) {
if (proc->pid <= 0) return;
LIST_INSERT_HEAD(&server->procs, proc, entry);
server->client_count++;
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));
}
}
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;
}
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++;
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;
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;
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));
}
// 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;
}
} 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 (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");
if (!pss->authenticated) {
lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, NULL, 0);
return -1;
}
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);
}
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;
}
if (spawn_process(pss) != 0) return 1;
break;
default:
break;
}
lwsl_warn("ignored unknown message type: %c\n", command);
break;
}
return 0;
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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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
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
uv_loop_t *loop; // the libuv event loop
uv_signal_t watcher; // SIGCHLD watcher
LIST_HEAD(proc, pty_proc) procs; // started process list
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);

View File

@@ -1,10 +1,10 @@
#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>
@@ -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);
}
}
// 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);
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);
}
}
// 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);
}

View File

@@ -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

View File

@@ -1,138 +1,124 @@
#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;
}

View File

@@ -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