mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-10 04:27:45 -05:00
Make foot able to receive a socket from its parent
If the argument to --server is parsed as a number, consider it to be a file descriptor, and use that as a socket. This is necessary to be able to use socket activation with the server mode of foot.
This commit is contained in:
parent
bd5576825f
commit
88a0f7397c
1 changed files with 92 additions and 27 deletions
119
server.c
119
server.c
|
|
@ -1,6 +1,7 @@
|
|||
#include "server.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
#include "client-protocol.h"
|
||||
#include "shm.h"
|
||||
#include "terminal.h"
|
||||
#include "util.h"
|
||||
#include "wayland.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
|
@ -425,44 +427,107 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct server *
|
||||
server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||
struct wayland *wayl)
|
||||
{
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
||||
if (fd == -1) {
|
||||
LOG_ERRNO("failed to create UNIX socket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fd;
|
||||
struct server *server = NULL;
|
||||
const char *sock_path = conf->server_socket_path;
|
||||
char *end;
|
||||
|
||||
switch (try_connect(sock_path)) {
|
||||
case CONNECT_FAIL:
|
||||
break;
|
||||
|
||||
case CONNECT_SUCCESS:
|
||||
LOG_ERR("%s is already accepting connections; is 'foot --server' already running", sock_path);
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case CONNECT_ERR:
|
||||
goto err;
|
||||
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;
|
||||
}
|
||||
|
||||
unlink(sock_path);
|
||||
switch (try_connect(sock_path)) {
|
||||
case CONNECT_FAIL:
|
||||
break;
|
||||
|
||||
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
||||
case CONNECT_SUCCESS:
|
||||
LOG_ERR("%s is already accepting connections; is 'foot --server' already running", sock_path);
|
||||
/* FALLTHROUGH */
|
||||
|
||||
if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
LOG_ERRNO("%s: failed to bind", addr.sun_path);
|
||||
goto err;
|
||||
}
|
||||
case CONNECT_ERR:
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (listen(fd, 0) < 0) {
|
||||
LOG_ERRNO("%s: failed to listen", addr.sun_path);
|
||||
goto err;
|
||||
unlink(sock_path);
|
||||
|
||||
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
server = malloc(sizeof(*server));
|
||||
|
|
@ -487,7 +552,7 @@ server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server))
|
||||
goto err;
|
||||
|
||||
LOG_INFO("accepting connections on %s", sock_path);
|
||||
LOG_INFO("accepting connections on %s", sock_path != NULL ? sock_path : "socket provided through socket activation");
|
||||
|
||||
return server;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue