diff --git a/client-protocol.h b/client-protocol.h index 663199c9..c1291f28 100644 --- a/client-protocol.h +++ b/client-protocol.h @@ -4,32 +4,23 @@ #include #include -struct client_argv { +struct client_string { uint16_t len; - /* char arg[static len]; */ + /* char str[static len]; */ }; struct client_data { - unsigned width; - unsigned height; - uint8_t size_type:1; // Values correspond to enum conf_size_type - uint8_t maximized:1; - uint8_t fullscreen:1; - uint8_t hold:1; - uint8_t login_shell:1; - uint8_t no_wait:1; + bool hold:1; + bool no_wait:1; + uint8_t reserved:6; uint16_t cwd_len; - uint16_t term_len; - uint16_t title_len; - uint16_t app_id_len; - + uint16_t override_count; uint16_t argc; /* char cwd[static cwd_len]; */ - /* char term[static term_len]; */ - /* char title[static title_len]; */ - /* char app_id[static app_id_len]; */ + /* struct client_string overrides[static override_count]; */ + /* struct client_string argv[static argc]; */ +} __attribute__((packed)); - /* struct client_argv argv[static argc]; */ -}; +_Static_assert(sizeof(struct client_data) == 7, "protocol struct size error"); diff --git a/client.c b/client.c index 1445146c..9dfeda41 100644 --- a/client.c +++ b/client.c @@ -12,6 +12,8 @@ #include #include +#include + #define LOG_MODULE "foot-client" #define LOG_ENABLE_DBG 0 #include "log.h" @@ -96,22 +98,22 @@ main(int argc, char *const *argv) {NULL, no_argument, NULL, 0}, }; - const char *term = ""; - const char *title = ""; - const char *app_id = ""; + tll(char *) overrides = tll_init(); + const char *custom_cwd = NULL; - unsigned size_type = 0; // enum conf_size_type (without pulling in tllist/fcft via config.h) - unsigned width = 0; - unsigned height = 0; const char *server_socket_path = NULL; enum log_class log_level = LOG_CLASS_INFO; enum log_colorize log_colorize = LOG_COLORIZE_AUTO; - bool login_shell = false; - bool maximized = false; - bool fullscreen = false; bool hold = false; bool no_wait = false; + char buf[1024]; + + /* malloc:ed and needs to be in scope of all goto's */ + char *_cwd = NULL; + struct client_string *coverrides = NULL; + struct client_string *cargv = NULL; + while (true) { int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNd:l::vh", longopts, NULL); if (c == -1) @@ -119,55 +121,62 @@ main(int argc, char *const *argv) switch (c) { case 't': - term = optarg; + snprintf(buf, sizeof(buf), "term=%s", optarg); + tll_push_back(overrides, xstrdup(buf)); break; case 'T': - title = optarg; + snprintf(buf, sizeof(buf), "title=%s", optarg); + tll_push_back(overrides, xstrdup(buf)); break; case 'a': - app_id = optarg; + snprintf(buf, sizeof(buf), "app-id=%s", optarg); + tll_push_back(overrides, xstrdup(buf)); break; case 'L': - login_shell = true; + tll_push_back(overrides, xstrdup("login-shell=yes")); break; case 'D': { struct stat st; if (stat(optarg, &st) < 0 || !(st.st_mode & S_IFDIR)) { fprintf(stderr, "error: %s: not a directory\n", optarg); - return ret; + goto err; } custom_cwd = optarg; break; } - case 'w': + case 'w': { + unsigned width, height; if (sscanf(optarg, "%ux%u", &width, &height) != 2 || width == 0 || height == 0) { fprintf(stderr, "error: invalid window-size-pixels: %s\n", optarg); return ret; } - size_type = 0; // CONF_SIZE_PX + snprintf(buf, sizeof(buf), "initial-window-size-pixels=%ux%u", width, height); + tll_push_back(overrides, xstrdup(buf)); break; + } - case 'W': + case 'W': { + unsigned width, height; if (sscanf(optarg, "%ux%u", &width, &height) != 2 || width == 0 || height == 0) { fprintf(stderr, "error: invalid window-size-chars: %s\n", optarg); - return ret; + goto err; } - size_type = 1; // CONF_SIZE_CELLS + snprintf(buf, sizeof(buf), "initial-window-size-chars=%ux%u", width, height); + tll_push_back(overrides, xstrdup(buf)); break; + } case 'm': - maximized = true; - fullscreen = false; + tll_push_back(overrides, xstrdup("initial-window-mode=maximized")); break; case 'F': - fullscreen = true; - maximized = false; + tll_push_back(overrides, xstrdup("initial-window-mode=fullscreen")); break; case 's': @@ -190,7 +199,7 @@ main(int argc, char *const *argv) "-d,--log-level: %s: argument must be one of %s\n", optarg, log_level_string_hint()); - return ret; + goto err; } log_level = lvl; break; @@ -205,20 +214,22 @@ main(int argc, char *const *argv) log_colorize = LOG_COLORIZE_ALWAYS; else { fprintf(stderr, "%s: argument must be one of 'never', 'always' or 'auto'\n", optarg); - return ret; + goto err; } break; case 'v': printf("footclient %s\n", version_and_features()); - return EXIT_SUCCESS; + ret = EXIT_SUCCESS; + goto err; case 'h': print_usage(prog_name); - return EXIT_SUCCESS; + ret = EXIT_SUCCESS; + goto err; case '?': - return ret; + goto err; } } @@ -227,10 +238,6 @@ main(int argc, char *const *argv) log_init(log_colorize, false, LOG_FACILITY_USER, log_level); - /* malloc:ed and needs to be in scope of all goto's */ - char *_cwd = NULL; - struct client_argv *cargv = NULL; - int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { LOG_ERRNO("failed to create socket"); @@ -290,33 +297,36 @@ main(int argc, char *const *argv) /* String lengths, including NULL terminator */ const size_t cwd_len = strlen(cwd) + 1; - const size_t term_len = strlen(term) + 1; - const size_t title_len = strlen(title) + 1; - const size_t app_id_len = strlen(app_id) + 1; + const size_t override_count = tll_length(overrides); const struct client_data data = { - .width = width, - .height = height, - .size_type = size_type, - .maximized = maximized, - .fullscreen = fullscreen, .hold = hold, - .login_shell = login_shell, .no_wait = no_wait, .cwd_len = cwd_len, - .term_len = term_len, - .title_len = title_len, - .app_id_len = app_id_len, + .override_count = override_count, .argc = argc, }; - /* Total packet length, not (yet) including argv[] */ - uint64_t total_len = ( - sizeof(data) + - cwd_len + - term_len + - title_len + - app_id_len); + /* Total packet length, not (yet) including overrides or argv[] */ + uint64_t total_len = sizeof(data) + cwd_len; + + /* Add overrides to total packet length */ + coverrides = xmalloc(override_count * sizeof(coverrides[0])); + { + size_t i = 0; + tll_foreach(overrides, it) { + const size_t len = strlen(it->item) + 1; + + if (len >= 1 << (8 * sizeof(coverrides[i].len))) { + LOG_ERR("override length overflow"); + goto err; + } + + coverrides[i].len = len; + total_len += sizeof(coverrides[i]) + coverrides[i].len; + i++; + } + } cargv = xmalloc(argc * sizeof(cargv[0])); @@ -336,9 +346,7 @@ main(int argc, char *const *argv) /* Check for size overflows */ if (total_len >= 1llu << (8 * sizeof(uint32_t)) || cwd_len >= 1 << (8 * sizeof(data.cwd_len)) || - term_len >= 1 << (8 * sizeof(data.term_len)) || - title_len >= 1 << (8 * sizeof(data.title_len)) || - app_id_len >= 1 << (8 * sizeof(data.app_id_len)) || + override_count > (size_t)(unsigned int)data.override_count || argc > (int)(unsigned int)data.argc) { LOG_ERR("size overflow"); @@ -348,21 +356,33 @@ main(int argc, char *const *argv) /* Send everything except argv[] */ if (send(fd, &(uint32_t){total_len}, sizeof(uint32_t), 0) != sizeof(uint32_t) || send(fd, &data, sizeof(data), 0) != sizeof(data) || - send(fd, cwd, cwd_len, 0) != cwd_len || - send(fd, term, term_len, 0) != term_len || - send(fd, title, title_len, 0) != title_len || - send(fd, app_id, app_id_len, 0) != app_id_len) + send(fd, cwd, cwd_len, 0) != cwd_len) { LOG_ERRNO("failed to send setup packet to server"); goto err; } + /* Send overrides */ + { + size_t i = 0; + tll_foreach(overrides, it) { + if (send(fd, &coverrides[i], sizeof(coverrides[i]), 0) != sizeof(coverrides[i]) || + send(fd, it->item, coverrides[i].len, 0) != coverrides[i].len) + { + LOG_ERRNO("failed to send setup packet (overrides) to server"); + goto err; + } + + i++; + } + } + /* Send argv[] */ for (size_t i = 0; i < argc; i++) { if (send(fd, &cargv[i], sizeof(cargv[i]), 0) != sizeof(cargv[i]) || send(fd, argv[i], cargv[i].len, 0) != cargv[i].len) { - LOG_ERRNO("failed to send setup packet to server"); + LOG_ERRNO("failed to send setup packet (argv) to server"); goto err; } } @@ -386,7 +406,9 @@ main(int argc, char *const *argv) ret = exit_code; err: + tll_free_and_free(overrides, free); free(cargv); + free(coverrides); free(_cwd); if (fd != -1) close(fd); diff --git a/meson.build b/meson.build index 0c48929a..479f5747 100644 --- a/meson.build +++ b/meson.build @@ -211,6 +211,7 @@ executable( 'macros.h', 'util.h', version, + dependencies: [tllist], link_with: common, install: true) diff --git a/server.c b/server.c index 678672ad..36bb0ddc 100644 --- a/server.c +++ b/server.c @@ -240,6 +240,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) uint8_t *p = client->buffer.data; const uint8_t *end = &client->buffer.data[client->buffer.idx]; + config_override_t overrides = tll_init(); struct client_data cdata; CHECK_BUF(sizeof(cdata)); @@ -250,22 +251,23 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) const char *cwd = (const char *)p; p += cdata.cwd_len; LOG_DBG("CWD = %.*s", cdata.cwd_len, cwd); - CHECK_BUF_AND_NULL(cdata.term_len); - const char *term_env = (const char *)p; p += cdata.term_len; - LOG_DBG("TERM = %.*s", cdata.term_len, term_env); + /* Overrides */ + for (uint16_t i = 0; i < cdata.override_count; i++) { + const struct client_string *arg = (const struct client_string *)p; + CHECK_BUF(sizeof(*arg)); + p += sizeof(*arg); - CHECK_BUF_AND_NULL(cdata.title_len); - const char *title = (const char *)p; p += cdata.title_len; - LOG_DBG("title = %.*s", cdata.title_len, title); + CHECK_BUF_AND_NULL(arg->len); + const char *str = (const char *)p; + p += arg->len; - CHECK_BUF_AND_NULL(cdata.app_id_len); - const char *app_id = (const char *)p; p += cdata.app_id_len; - LOG_DBG("app-id = %.*s", cdata.app_id_len, app_id); + tll_push_back(overrides, xstrdup(str)); + } + /* argv */ argv = xcalloc(cdata.argc + 1, sizeof(argv[0])); - for (uint16_t i = 0; i < cdata.argc; i++) { - struct client_argv arg; + struct client_string arg; CHECK_BUF(sizeof(arg)); memcpy(&arg, p, sizeof(arg)); p += sizeof(arg); @@ -282,63 +284,14 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) struct terminal_instance *instance = malloc(sizeof(struct terminal_instance)); const bool need_to_clone_conf = - strlen(term_env) > 0 || - strlen(title) > 0 || - strlen(app_id) > 0 || + tll_length(overrides)> 0 || cdata.hold != server->conf->hold_at_exit || - cdata.login_shell != server->conf->login_shell || - cdata.no_wait != server->conf->no_wait || - (cdata.maximized && server->conf->startup_mode != STARTUP_MAXIMIZED) || - (cdata.fullscreen && server->conf->startup_mode != STARTUP_FULLSCREEN) || - (cdata.width > 0 && cdata.height > 0); + cdata.no_wait != server->conf->no_wait; struct config *conf = NULL; if (need_to_clone_conf) { conf = config_clone(server->conf); - char buf[1024]; - config_override_t overrides = tll_init(); - - if (strlen(term_env) > 0) { - snprintf(buf, sizeof(buf), "term=%s", term_env); - tll_push_back(overrides, xstrdup(buf)); - } - - if (strlen(title) > 0) { - snprintf(buf, sizeof(buf), "title=%s", title); - tll_push_back(overrides, xstrdup(buf)); - } - - if (strlen(app_id)> 0) { - snprintf(buf, sizeof(buf), "app-id=%s", app_id); - tll_push_back(overrides, xstrdup(buf)); - } - - if (cdata.login_shell != server->conf->login_shell) - tll_push_back(overrides, xstrdup("login-shell=yes")); - - if (cdata.maximized && server->conf->startup_mode != STARTUP_MAXIMIZED) - tll_push_back(overrides, xstrdup("initial-window-mode=maximized")); - - if (cdata.fullscreen && server->conf->startup_mode != STARTUP_FULLSCREEN) - tll_push_back(overrides, xstrdup("initial-window-mode=fullscreen")); - - if (cdata.width > 0 && cdata.height > 0) { - switch (cdata.size_type) { - case CONF_SIZE_PX: - snprintf(buf, sizeof(buf), "initial-window-size-pixels=%ux%u", - cdata.width, cdata.height); - tll_push_back(overrides, xstrdup(buf)); - break; - - case CONF_SIZE_CELLS: - snprintf(buf, sizeof(buf), "initial-window-size-chars=%ux%u", - cdata.width, cdata.height); - tll_push_back(overrides, xstrdup(buf)); - break; - } - } - if (cdata.no_wait != server->conf->no_wait) conf->no_wait = cdata.no_wait; @@ -346,7 +299,6 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) conf->hold_at_exit = cdata.hold; config_override_apply(conf, &overrides, false); - tll_free_and_free(overrides, free); } *instance = (struct terminal_instance) { @@ -377,6 +329,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) instance->client = client; client->instance = instance; free(argv); + tll_free_and_free(overrides, free); } return true; @@ -385,6 +338,7 @@ shutdown: LOG_DBG("client FD=%d: disconnected", client->fd); free(argv); + tll_free_and_free(overrides, free); fdm_del(fdm, fd); client->fd = -1;