2019-11-01 20:37:22 +01:00
|
|
|
#include "server.h"
|
|
|
|
|
|
2019-12-01 19:22:45 +01:00
|
|
|
#include <string.h>
|
2022-01-15 16:58:32 +01:00
|
|
|
#include <fcntl.h>
|
2019-11-01 20:37:22 +01:00
|
|
|
#include <unistd.h>
|
2019-11-02 23:25:14 +01:00
|
|
|
#include <errno.h>
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
|
|
2021-01-19 14:34:30 +00:00
|
|
|
#include <sys/un.h>
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2019-11-17 19:19:55 +01:00
|
|
|
#include <tllist.h>
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
#define LOG_MODULE "server"
|
2019-11-05 14:36:58 +01:00
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-11-01 20:37:22 +01:00
|
|
|
#include "log.h"
|
2019-11-02 00:49:25 +01:00
|
|
|
|
2020-11-21 20:21:18 +01:00
|
|
|
#include "client-protocol.h"
|
2019-11-01 20:37:22 +01:00
|
|
|
#include "terminal.h"
|
2022-01-15 16:58:32 +01:00
|
|
|
#include "util.h"
|
2019-11-02 00:49:25 +01:00
|
|
|
#include "wayland.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
#include "xmalloc.h"
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
struct client;
|
2021-03-12 20:46:55 -03:00
|
|
|
struct terminal_instance;
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
struct server {
|
|
|
|
|
const struct config *conf;
|
|
|
|
|
struct fdm *fdm;
|
2020-05-21 20:17:29 +02:00
|
|
|
struct reaper *reaper;
|
2019-11-01 20:37:22 +01:00
|
|
|
struct wayland *wayl;
|
|
|
|
|
|
|
|
|
|
int fd;
|
2019-12-14 12:59:38 +01:00
|
|
|
const char *sock_path;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
tll(struct client *) clients;
|
2021-03-12 20:46:55 -03:00
|
|
|
tll(struct terminal_instance *) terminals;
|
2019-11-01 20:37:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct client {
|
|
|
|
|
struct server *server;
|
|
|
|
|
int fd;
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
struct {
|
|
|
|
|
uint8_t *data;
|
|
|
|
|
size_t left;
|
|
|
|
|
size_t idx;
|
|
|
|
|
} buffer;
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
struct terminal_instance *instance;
|
|
|
|
|
};
|
2021-03-13 07:41:05 -03:00
|
|
|
static void client_destroy(struct client *client);
|
2021-03-12 20:46:55 -03:00
|
|
|
|
|
|
|
|
struct terminal_instance {
|
|
|
|
|
struct terminal *terminal;
|
|
|
|
|
struct server *server;
|
|
|
|
|
struct client *client;
|
2021-06-17 18:11:16 +02:00
|
|
|
struct config *conf;
|
2019-11-01 20:37:22 +01:00
|
|
|
};
|
2021-03-13 07:41:05 -03:00
|
|
|
static void instance_destroy(struct terminal_instance *instance, int exit_code);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
client_destroy(struct client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
if (client->instance != NULL) {
|
2019-11-01 20:37:22 +01:00
|
|
|
LOG_WARN("client FD=%d: terminal still alive", client->fd);
|
2021-03-12 20:46:55 -03:00
|
|
|
client->instance->client = NULL;
|
|
|
|
|
instance_destroy(client->instance, 1);
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->fd != -1) {
|
|
|
|
|
LOG_DBG("client FD=%d: disconnected", client->fd);
|
|
|
|
|
fdm_del(client->server->fdm, client->fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tll_foreach(client->server->clients, it) {
|
|
|
|
|
if (it->item == client) {
|
|
|
|
|
tll_remove(client->server->clients, it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
free(client->buffer.data);
|
2019-11-01 20:37:22 +01:00
|
|
|
free(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
client_send_exit_code(struct client *client, int exit_code)
|
|
|
|
|
{
|
|
|
|
|
if (client->fd == -1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (write(client->fd, &exit_code, sizeof(exit_code)) != sizeof(exit_code))
|
|
|
|
|
LOG_ERRNO("failed to write slave exit code to client");
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
static void
|
|
|
|
|
instance_destroy(struct terminal_instance *instance, int exit_code)
|
|
|
|
|
{
|
2021-03-13 07:41:05 -03:00
|
|
|
if (instance->terminal != NULL)
|
2021-03-12 20:46:55 -03:00
|
|
|
term_destroy(instance->terminal);
|
|
|
|
|
|
|
|
|
|
tll_foreach(instance->server->terminals, it) {
|
|
|
|
|
if (it->item == instance) {
|
|
|
|
|
tll_remove(instance->server->terminals, it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instance->client != NULL) {
|
|
|
|
|
instance->client->instance = NULL;
|
|
|
|
|
client_send_exit_code(instance->client, exit_code);
|
|
|
|
|
client_destroy(instance->client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: clone server conf completely, so that we can just call
|
|
|
|
|
* conf_destroy() here */
|
2021-06-17 18:11:16 +02:00
|
|
|
if (instance->conf != NULL) {
|
2022-04-12 13:01:56 +02:00
|
|
|
config_free(instance->conf);
|
2021-06-23 13:18:55 +02:00
|
|
|
free(instance->conf);
|
2021-06-17 18:11:16 +02:00
|
|
|
}
|
2021-03-12 20:46:55 -03:00
|
|
|
free(instance);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
static void
|
|
|
|
|
term_shutdown_handler(void *data, int exit_code)
|
|
|
|
|
{
|
2021-03-12 20:46:55 -03:00
|
|
|
struct terminal_instance *instance = data;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
instance->terminal = NULL;
|
|
|
|
|
instance_destroy(instance, exit_code);
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
|
|
|
|
struct server *server = client->server;
|
2019-11-05 09:31:14 +01:00
|
|
|
|
|
|
|
|
char **argv = NULL;
|
2021-06-23 15:38:29 +02:00
|
|
|
config_override_t overrides = tll_init();
|
2022-04-11 12:19:40 +02:00
|
|
|
char **envp = NULL;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
if (events & EPOLLHUP)
|
|
|
|
|
goto shutdown;
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(events & EPOLLIN);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
if (client->instance != NULL) {
|
2019-11-05 09:31:47 +01:00
|
|
|
uint8_t dummy[128];
|
2019-11-05 10:08:30 +01:00
|
|
|
ssize_t count = read(fd, dummy, sizeof(dummy));
|
|
|
|
|
LOG_WARN("client unexpectedly sent %zd bytes", count);
|
|
|
|
|
return true; /* TODO: shutdown instead? */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->buffer.data == NULL) {
|
|
|
|
|
/*
|
|
|
|
|
* We haven't received any data yet - the first thing the
|
|
|
|
|
* client sends is the total size of the initialization
|
|
|
|
|
* data.
|
|
|
|
|
*/
|
|
|
|
|
uint32_t total_len;
|
2020-01-10 19:27:17 +01:00
|
|
|
ssize_t count = recv(fd, &total_len, sizeof(total_len), 0);
|
|
|
|
|
if (count < 0) {
|
2019-11-05 10:08:30 +01:00
|
|
|
LOG_ERRNO("failed to read total length");
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-10 19:27:17 +01:00
|
|
|
if (count != sizeof(total_len)) {
|
|
|
|
|
LOG_ERR("client did not send setup packet size");
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint32_t max_size = 128 * 1024;
|
|
|
|
|
if (total_len > max_size) {
|
|
|
|
|
LOG_ERR("client wants to send too large setup packet (%u > %u)",
|
|
|
|
|
total_len, max_size);
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
LOG_DBG("total len: %u", total_len);
|
2020-08-08 20:34:30 +01:00
|
|
|
client->buffer.data = xmalloc(total_len + 1);
|
2019-11-05 10:08:30 +01:00
|
|
|
client->buffer.left = total_len;
|
|
|
|
|
client->buffer.idx = 0;
|
|
|
|
|
|
|
|
|
|
/* Prevent our strlen() calls to run outside */
|
|
|
|
|
client->buffer.data[total_len] = '\0';
|
|
|
|
|
return true; /* Let FDM trigger again when we have more data */
|
2019-11-05 09:31:47 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
/* Keep filling our buffer of initialization data */
|
|
|
|
|
ssize_t count = recv(
|
|
|
|
|
fd, &client->buffer.data[client->buffer.idx], client->buffer.left, 0);
|
2019-11-05 10:08:30 +01:00
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
if (count < 0) {
|
|
|
|
|
LOG_ERRNO("failed to read");
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
2019-11-01 21:10:47 +01:00
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
client->buffer.idx += count;
|
|
|
|
|
client->buffer.left -= count;
|
2019-11-05 10:08:30 +01:00
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
if (client->buffer.left > 0) {
|
|
|
|
|
/* Not done yet */
|
|
|
|
|
return true;
|
2019-11-02 12:17:30 +01:00
|
|
|
}
|
2019-11-01 21:10:47 +01:00
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
/* All initialization data received - time to instantiate a terminal! */
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
xassert(client->instance == NULL);
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(client->buffer.data != NULL);
|
|
|
|
|
xassert(client->buffer.left == 0);
|
2019-11-05 10:08:30 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Parse the received buffer, verifying lengths etc
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define CHECK_BUF(sz) do { \
|
|
|
|
|
if (p + (sz) > end) \
|
|
|
|
|
goto shutdown; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2020-11-21 20:27:13 +01:00
|
|
|
#define CHECK_BUF_AND_NULL(sz) do { \
|
|
|
|
|
CHECK_BUF(sz); \
|
|
|
|
|
if (sz == 0) \
|
|
|
|
|
goto shutdown; \
|
|
|
|
|
if (p[sz - 1] != '\0') \
|
|
|
|
|
goto shutdown; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
uint8_t *p = client->buffer.data;
|
|
|
|
|
const uint8_t *end = &client->buffer.data[client->buffer.idx];
|
|
|
|
|
|
2020-11-21 20:47:17 +01:00
|
|
|
struct client_data cdata;
|
|
|
|
|
CHECK_BUF(sizeof(cdata));
|
|
|
|
|
memcpy(&cdata, p, sizeof(cdata));
|
|
|
|
|
p += sizeof(cdata);
|
2019-12-21 19:56:37 +01:00
|
|
|
|
2020-11-21 20:47:17 +01:00
|
|
|
CHECK_BUF_AND_NULL(cdata.cwd_len);
|
|
|
|
|
const char *cwd = (const char *)p; p += cdata.cwd_len;
|
|
|
|
|
LOG_DBG("CWD = %.*s", cdata.cwd_len, cwd);
|
2019-12-21 19:56:37 +01:00
|
|
|
|
2021-10-28 17:51:44 -07:00
|
|
|
/* XDGA token */
|
|
|
|
|
const char *token = NULL;
|
|
|
|
|
if (cdata.xdga_token) {
|
|
|
|
|
|
|
|
|
|
CHECK_BUF_AND_NULL(cdata.token_len);
|
|
|
|
|
token = (const char *)p; p += cdata.token_len;
|
|
|
|
|
LOG_DBG("XDGA = %.*s", cdata.token_len, token);
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DBG("No XDGA token");
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-23 14:22:18 +02:00
|
|
|
/* Overrides */
|
|
|
|
|
for (uint16_t i = 0; i < cdata.override_count; i++) {
|
2021-06-23 14:30:02 +02:00
|
|
|
struct client_string arg;
|
|
|
|
|
CHECK_BUF(sizeof(arg));
|
|
|
|
|
memcpy(&arg, p, sizeof(arg)); p += sizeof(arg);
|
2020-04-01 19:59:47 +02:00
|
|
|
|
2021-06-23 14:30:02 +02:00
|
|
|
CHECK_BUF_AND_NULL(arg.len);
|
2021-06-23 14:22:18 +02:00
|
|
|
const char *str = (const char *)p;
|
2021-06-23 14:30:02 +02:00
|
|
|
p += arg.len;
|
2020-04-01 19:59:47 +02:00
|
|
|
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_push_back(overrides, xstrdup(str));
|
|
|
|
|
}
|
2020-04-01 19:59:47 +02:00
|
|
|
|
2021-06-23 14:22:18 +02:00
|
|
|
/* argv */
|
2020-11-21 20:47:17 +01:00
|
|
|
argv = xcalloc(cdata.argc + 1, sizeof(argv[0]));
|
|
|
|
|
for (uint16_t i = 0; i < cdata.argc; i++) {
|
2021-06-23 14:22:18 +02:00
|
|
|
struct client_string arg;
|
2020-11-21 20:47:17 +01:00
|
|
|
CHECK_BUF(sizeof(arg));
|
2021-06-23 14:30:02 +02:00
|
|
|
memcpy(&arg, p, sizeof(arg)); p += sizeof(arg);
|
2020-04-01 18:40:51 +02:00
|
|
|
|
2020-11-21 20:47:17 +01:00
|
|
|
CHECK_BUF_AND_NULL(arg.len);
|
|
|
|
|
argv[i] = (char *)p; p += arg.len;
|
|
|
|
|
LOG_DBG("argv[%hu] = %.*s", i, arg.len, argv[i]);
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
2022-04-11 12:19:40 +02:00
|
|
|
|
|
|
|
|
/* envp */
|
|
|
|
|
envp = cdata.env_count != 0
|
|
|
|
|
? xcalloc(cdata.env_count + 1, sizeof(envp[0]))
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
for (uint16_t i = 0; i < cdata.env_count; i++) {
|
|
|
|
|
struct client_string e;
|
|
|
|
|
CHECK_BUF(sizeof(e));
|
|
|
|
|
memcpy(&e, p, sizeof(e)); p += sizeof(e);
|
|
|
|
|
|
|
|
|
|
CHECK_BUF_AND_NULL(e.len);
|
|
|
|
|
envp[i] = (char *)p; p += e.len;
|
|
|
|
|
LOG_DBG("env[%hu] = %.*s", i, e.len, envp[i]);
|
|
|
|
|
}
|
2019-11-05 10:08:30 +01:00
|
|
|
|
2020-11-21 20:27:13 +01:00
|
|
|
#undef CHECK_BUF_AND_NULL
|
2019-11-05 10:08:30 +01:00
|
|
|
#undef CHECK_BUF
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2024-01-25 07:03:50 +00:00
|
|
|
struct terminal_instance *instance = xmalloc(sizeof(struct terminal_instance));
|
2021-03-12 20:46:55 -03:00
|
|
|
|
2021-06-17 18:11:16 +02:00
|
|
|
const bool need_to_clone_conf =
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_length(overrides)> 0 ||
|
2021-09-05 09:28:19 +02:00
|
|
|
cdata.hold != server->conf->hold_at_exit;
|
2021-06-17 18:11:16 +02:00
|
|
|
|
|
|
|
|
struct config *conf = NULL;
|
|
|
|
|
if (need_to_clone_conf) {
|
2021-06-23 13:18:55 +02:00
|
|
|
conf = config_clone(server->conf);
|
|
|
|
|
|
|
|
|
|
if (cdata.hold != server->conf->hold_at_exit)
|
|
|
|
|
conf->hold_at_exit = cdata.hold;
|
|
|
|
|
|
|
|
|
|
config_override_apply(conf, &overrides, false);
|
2021-08-31 19:52:31 +02:00
|
|
|
|
|
|
|
|
if (conf->tweak.font_monospace_warn && conf->fonts[0].count > 0) {
|
|
|
|
|
check_if_font_is_monospaced(
|
|
|
|
|
conf->fonts[0].arr[0].pattern,
|
|
|
|
|
&conf->notifications);
|
|
|
|
|
}
|
2021-06-17 18:11:16 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
*instance = (struct terminal_instance) {
|
2021-03-13 07:41:05 -03:00
|
|
|
.client = NULL,
|
2021-03-12 20:46:55 -03:00
|
|
|
.server = server,
|
2021-06-17 18:11:16 +02:00
|
|
|
.conf = conf,
|
2021-03-12 20:46:55 -03:00
|
|
|
};
|
2020-11-22 00:12:23 +00:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
instance->terminal = term_init(
|
2021-06-17 18:11:16 +02:00
|
|
|
conf != NULL ? conf : server->conf,
|
2021-10-28 17:51:44 -07:00
|
|
|
server->fdm, server->reaper, server->wayl, "footclient", cwd, token,
|
2021-12-10 17:40:59 +00:00
|
|
|
NULL, cdata.argc, argv, (const char *const *)envp,
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
&term_shutdown_handler, instance);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
if (instance->terminal == NULL) {
|
2019-11-01 20:37:22 +01:00
|
|
|
LOG_ERR("failed to instantiate new terminal");
|
2021-05-01 10:46:40 +02:00
|
|
|
client_send_exit_code(client, -26);
|
2021-03-12 20:46:55 -03:00
|
|
|
instance_destroy(instance, -1);
|
2019-11-01 20:37:22 +01:00
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-05 09:28:19 +02:00
|
|
|
if (cdata.no_wait) {
|
2021-03-12 20:46:55 -03:00
|
|
|
// the server owns the instance
|
|
|
|
|
tll_push_back(server->terminals, instance);
|
|
|
|
|
client_send_exit_code(client, 0);
|
|
|
|
|
goto shutdown;
|
|
|
|
|
} else {
|
|
|
|
|
// the instance is attached to the client
|
|
|
|
|
instance->client = client;
|
|
|
|
|
client->instance = instance;
|
2021-03-13 07:41:05 -03:00
|
|
|
free(argv);
|
2022-04-11 12:19:40 +02:00
|
|
|
free(envp);
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_free_and_free(overrides, free);
|
2021-03-12 20:46:55 -03:00
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
shutdown:
|
|
|
|
|
LOG_DBG("client FD=%d: disconnected", client->fd);
|
|
|
|
|
|
2019-11-05 09:31:14 +01:00
|
|
|
free(argv);
|
2022-04-11 12:19:40 +02:00
|
|
|
free(envp);
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_free_and_free(overrides, free);
|
2019-11-01 20:37:22 +01:00
|
|
|
fdm_del(fdm, fd);
|
|
|
|
|
client->fd = -1;
|
|
|
|
|
|
2021-07-31 19:08:51 +02:00
|
|
|
if (client->instance != NULL &&
|
|
|
|
|
!client->instance->terminal->shutdown.in_progress)
|
|
|
|
|
{
|
2021-03-12 20:46:55 -03:00
|
|
|
term_shutdown(client->instance->terminal);
|
2021-07-31 19:08:51 +02:00
|
|
|
} else
|
2019-11-01 20:37:22 +01:00
|
|
|
client_destroy(client);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
fdm_server(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
{
|
|
|
|
|
if (events & EPOLLHUP)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
struct server *server = data;
|
|
|
|
|
|
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
|
socklen_t addr_size = sizeof(addr);
|
|
|
|
|
int client_fd = accept4(
|
2020-01-10 19:27:17 +01:00
|
|
|
server->fd, (struct sockaddr *)&addr, &addr_size, SOCK_CLOEXEC | SOCK_NONBLOCK);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
if (client_fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to accept client connection");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-08 20:34:30 +01:00
|
|
|
struct client *client = xmalloc(sizeof(*client));
|
2019-11-01 20:37:22 +01:00
|
|
|
*client = (struct client) {
|
|
|
|
|
.server = server,
|
|
|
|
|
.fd = client_fd,
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
if (!fdm_add(server->fdm, client_fd, EPOLLIN, &fdm_client, client)) {
|
2019-11-01 20:37:22 +01:00
|
|
|
close(client_fd);
|
|
|
|
|
free(client);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("client FD=%d: connected", client_fd);
|
|
|
|
|
tll_push_back(server->clients, client);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum connect_status {CONNECT_ERR, CONNECT_FAIL, CONNECT_SUCCESS};
|
|
|
|
|
|
|
|
|
|
static enum connect_status
|
|
|
|
|
try_connect(const char *sock_path)
|
|
|
|
|
{
|
|
|
|
|
enum connect_status ret = CONNECT_ERR;
|
|
|
|
|
|
2019-11-03 01:48:40 +01:00
|
|
|
int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
2019-11-01 20:37:22 +01:00
|
|
|
if (fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to create UNIX socket");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
|
|
|
|
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
|
|
|
|
|
2019-11-02 23:25:14 +01:00
|
|
|
switch (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) {
|
|
|
|
|
case 0:
|
|
|
|
|
ret = CONNECT_SUCCESS;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
|
LOG_DBG("connect() failed: %s", strerror(errno));
|
|
|
|
|
ret = CONNECT_FAIL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close(fd);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
static bool
|
|
|
|
|
prepare_socket(int fd)
|
|
|
|
|
{
|
|
|
|
|
int flags = fcntl(fd, F_GETFD);
|
|
|
|
|
if (flags < 0) {
|
|
|
|
|
LOG_ERRNO("failed to get file descriptors flag for passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
|
|
|
|
LOG_ERRNO("failed to set FD_CLOEXEC for passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flags = fcntl(fd, F_GETFL);
|
|
|
|
|
if (flags < 0) {
|
|
|
|
|
LOG_ERRNO("failed to get file status flags for passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
|
|
|
|
LOG_ERRNO("failed to set non-blocking mode on passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int const socket_options[] = { SO_DOMAIN, SO_ACCEPTCONN, SO_TYPE };
|
|
|
|
|
int const socket_options_values[] = { AF_UNIX, 1, SOCK_STREAM};
|
|
|
|
|
char const * const socket_options_names[] = { "SO_DOMAIN", "SO_ACCEPTCONN", "SO_TYPE" };
|
|
|
|
|
|
|
|
|
|
xassert(ALEN(socket_options) == ALEN(socket_options_values));
|
|
|
|
|
xassert(ALEN(socket_options) == ALEN(socket_options_names));
|
|
|
|
|
|
|
|
|
|
int socket_option = 0;
|
|
|
|
|
socklen_t len;
|
|
|
|
|
for (size_t i = 0; i < ALEN(socket_options) ; i++) {
|
|
|
|
|
len = sizeof(socket_option);
|
|
|
|
|
if (getsockopt(fd, SOL_SOCKET, socket_options[i], &socket_option, &len) == -1 ||
|
|
|
|
|
len != sizeof(socket_option)) {
|
|
|
|
|
LOG_ERRNO("failed to read socket option from passed file descriptor");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (socket_option != socket_options_values[i]) {
|
|
|
|
|
LOG_ERR("wrong socket value for socket option '%s' on passed file descriptor",
|
|
|
|
|
socket_options_names[i]);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
struct server *
|
2020-05-21 20:17:29 +02:00
|
|
|
server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|
|
|
|
struct wayland *wayl)
|
2019-11-01 20:37:22 +01:00
|
|
|
{
|
2022-01-15 16:58:32 +01:00
|
|
|
int fd;
|
2019-11-01 20:37:22 +01:00
|
|
|
struct server *server = NULL;
|
2019-12-14 12:59:38 +01:00
|
|
|
const char *sock_path = conf->server_socket_path;
|
2022-01-15 16:58:32 +01:00
|
|
|
char *end;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
errno = 0;
|
|
|
|
|
fd = strtol(sock_path, &end, 10);
|
|
|
|
|
if (*end == '\0' && *sock_path != '\0')
|
|
|
|
|
{
|
|
|
|
|
if (!prepare_socket(fd))
|
|
|
|
|
goto err;
|
|
|
|
|
LOG_DBG("we've been started by socket activation, using passed socket");
|
|
|
|
|
sock_path = NULL;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LOG_DBG("no suitable pre-existing socket found, creating our own");
|
|
|
|
|
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to create UNIX socket");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
switch (try_connect(sock_path)) {
|
|
|
|
|
case CONNECT_FAIL:
|
|
|
|
|
break;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
case CONNECT_SUCCESS:
|
|
|
|
|
LOG_ERR("%s is already accepting connections; is 'foot --server' already running", sock_path);
|
|
|
|
|
/* FALLTHROUGH */
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
case CONNECT_ERR:
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
unlink(sock_path);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
|
|
|
|
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
|
|
|
LOG_ERRNO("%s: failed to bind", addr.sun_path);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (listen(fd, 0) < 0) {
|
|
|
|
|
LOG_ERRNO("%s: failed to listen", addr.sun_path);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
2019-11-02 23:25:14 +01:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
server = malloc(sizeof(*server));
|
2020-08-08 20:34:30 +01:00
|
|
|
if (unlikely(server == NULL)) {
|
|
|
|
|
LOG_ERRNO("malloc() failed");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
*server = (struct server) {
|
|
|
|
|
.conf = conf,
|
|
|
|
|
.fdm = fdm,
|
2020-05-21 20:17:29 +02:00
|
|
|
.reaper = reaper,
|
2019-11-01 20:37:22 +01:00
|
|
|
.wayl = wayl,
|
2019-11-02 23:25:14 +01:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
.fd = fd,
|
|
|
|
|
.sock_path = sock_path,
|
|
|
|
|
|
|
|
|
|
.clients = tll_init(),
|
2021-03-12 20:46:55 -03:00
|
|
|
.terminals = tll_init(),
|
2019-11-01 20:37:22 +01:00
|
|
|
};
|
|
|
|
|
|
2019-11-03 00:25:17 +01:00
|
|
|
if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server))
|
2019-11-01 20:37:22 +01:00
|
|
|
goto err;
|
|
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
LOG_INFO("accepting connections on %s", sock_path != NULL ? sock_path : "socket provided through socket activation");
|
2019-12-13 22:28:48 +01:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
return server;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
free(server);
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close(fd);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
server_destroy(struct server *server)
|
|
|
|
|
{
|
|
|
|
|
if (server == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("server destroy, %zu clients still alive",
|
|
|
|
|
tll_length(server->clients));
|
|
|
|
|
|
|
|
|
|
tll_foreach(server->clients, it) {
|
2021-05-01 10:46:40 +02:00
|
|
|
client_send_exit_code(it->item, -26);
|
2019-11-01 20:37:22 +01:00
|
|
|
client_destroy(it->item);
|
|
|
|
|
}
|
2021-03-13 07:41:05 -03:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
tll_free(server->clients);
|
|
|
|
|
|
2021-03-13 07:41:05 -03:00
|
|
|
tll_foreach(server->terminals, it)
|
2021-03-12 20:46:55 -03:00
|
|
|
instance_destroy(it->item, 1);
|
2021-03-13 07:41:05 -03:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
tll_free(server->terminals);
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
fdm_del(server->fdm, server->fd);
|
2019-11-02 23:32:13 +01:00
|
|
|
if (server->sock_path != NULL)
|
|
|
|
|
unlink(server->sock_path);
|
2019-11-01 20:37:22 +01:00
|
|
|
free(server);
|
|
|
|
|
}
|