mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
server/client: add support for sending SIGUSR to footclient
This patch adds the IPC infrastructure necessary to propagate SIGUSR1/SIGUSR2 from a footclient process to the server process. By targeting a particular footclient instance, only that particular instance changes theme. This is different from when targeting the server process, where all instances change theme. Closes #2156
This commit is contained in:
parent
70d99a8051
commit
b13a8f12d2
6 changed files with 151 additions and 12 deletions
|
|
@ -74,6 +74,10 @@
|
||||||
* Sending SIGUSR1/SIGUSR2 to a `foot --server` process now causes
|
* Sending SIGUSR1/SIGUSR2 to a `foot --server` process now causes
|
||||||
newly spawned client instances to use the selected theme, instead of
|
newly spawned client instances to use the selected theme, instead of
|
||||||
the original one.
|
the original one.
|
||||||
|
* SIGUSR1/SIGUSR2 can now be sent to `footclient` processes, to change
|
||||||
|
the theme of that particular instance ([#2156][2156]).
|
||||||
|
|
||||||
|
[2156]: https://codeberg.org/dnkl/foot/issues/2156
|
||||||
|
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,17 @@ struct client_data {
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
_Static_assert(sizeof(struct client_data) == 10, "protocol struct size error");
|
_Static_assert(sizeof(struct client_data) == 10, "protocol struct size error");
|
||||||
|
|
||||||
|
enum client_ipc_code {
|
||||||
|
FOOT_IPC_SIGUSR,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct client_ipc_hdr {
|
||||||
|
enum client_ipc_code ipc_code;
|
||||||
|
uint8_t size;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
|
||||||
|
struct client_ipc_sigusr {
|
||||||
|
int signo;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
|
||||||
65
client.c
65
client.c
|
|
@ -33,13 +33,20 @@ struct string {
|
||||||
typedef tll(struct string) string_list_t;
|
typedef tll(struct string) string_list_t;
|
||||||
|
|
||||||
static volatile sig_atomic_t aborted = 0;
|
static volatile sig_atomic_t aborted = 0;
|
||||||
|
static volatile sig_atomic_t sigusr = 0;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sig_handler(int signo)
|
sigint_handler(int signo)
|
||||||
{
|
{
|
||||||
aborted = 1;
|
aborted = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigusr_handler(int signo)
|
||||||
|
{
|
||||||
|
sigusr = signo;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
sendall(int sock, const void *_buf, size_t len)
|
sendall(int sock, const void *_buf, size_t len)
|
||||||
{
|
{
|
||||||
|
|
@ -507,15 +514,63 @@ main(int argc, char *const *argv)
|
||||||
if (!send_string_list(fd, &envp))
|
if (!send_string_list(fd, &envp))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
struct sigaction sa = {.sa_handler = &sig_handler};
|
struct sigaction sa_int = {.sa_handler = &sigint_handler};
|
||||||
sigemptyset(&sa.sa_mask);
|
struct sigaction sa_usr = {.sa_handler = &sigusr_handler};
|
||||||
if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0) {
|
sigemptyset(&sa_int.sa_mask);
|
||||||
|
sigemptyset(&sa_usr.sa_mask);
|
||||||
|
|
||||||
|
if (sigaction(SIGINT, &sa_int, NULL) < 0 ||
|
||||||
|
sigaction(SIGTERM, &sa_int, NULL) < 0 ||
|
||||||
|
sigaction(SIGUSR1, &sa_usr, NULL) < 0 ||
|
||||||
|
sigaction(SIGUSR2, &sa_usr, NULL) < 0)
|
||||||
|
{
|
||||||
LOG_ERRNO("failed to register signal handlers");
|
LOG_ERRNO("failed to register signal handlers");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int exit_code;
|
int exit_code;
|
||||||
ssize_t rcvd = recv(fd, &exit_code, sizeof(exit_code), 0);
|
ssize_t rcvd = -1;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
rcvd = recv(fd, &exit_code, sizeof(exit_code), 0);
|
||||||
|
|
||||||
|
const int got_sigusr = sigusr;
|
||||||
|
sigusr = 0;
|
||||||
|
|
||||||
|
if (rcvd < 0 && errno == EINTR) {
|
||||||
|
if (aborted)
|
||||||
|
break;
|
||||||
|
else if (got_sigusr != 0) {
|
||||||
|
LOG_DBG("sending sigusr %d to server", got_sigusr);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct client_ipc_hdr hdr;
|
||||||
|
struct client_ipc_sigusr sigusr;
|
||||||
|
} ipc = {
|
||||||
|
.hdr = {
|
||||||
|
.ipc_code = FOOT_IPC_SIGUSR,
|
||||||
|
.size = sizeof(struct client_ipc_sigusr),
|
||||||
|
},
|
||||||
|
.sigusr = {
|
||||||
|
.signo = got_sigusr,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ssize_t count = send(fd, &ipc, sizeof(ipc), 0);
|
||||||
|
if (count < 0) {
|
||||||
|
LOG_ERRNO("failed to send SIGUSR IPC to server");
|
||||||
|
goto err;
|
||||||
|
} else if ((size_t)count != sizeof(ipc)) {
|
||||||
|
LOG_ERR("failed to send SIGUSR IPC to server");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (rcvd == -1 && errno == EINTR)
|
if (rcvd == -1 && errno == EINTR)
|
||||||
xassert(aborted);
|
xassert(aborted);
|
||||||
|
|
|
||||||
|
|
@ -694,14 +694,14 @@ variables to unset may be defined in *foot.ini*(5).
|
||||||
The following signals have special meaning in foot:
|
The following signals have special meaning in foot:
|
||||||
|
|
||||||
- SIGUSR1: switch to color theme 1 (i.e. use the *[colors]* section).
|
- SIGUSR1: switch to color theme 1 (i.e. use the *[colors]* section).
|
||||||
- SIGUSR2: switch to color theme 2 (i.e. use the *[colors2]* section)-
|
- SIGUSR2: switch to color theme 2 (i.e. use the *[colors2]* section).
|
||||||
|
|
||||||
Note: you can send SIGUSR1/SIGUSR2 to a *foot --server* process too,
|
Note: you can send SIGUSR1/SIGUSR2 to a *foot --server* process too,
|
||||||
in which case all client instances will switch theme. Furthermore, all
|
in which case all client instances will switch theme. Furthermore, all
|
||||||
future client instances will also use the selected theme.
|
future client instances will also use the selected theme.
|
||||||
|
|
||||||
Sending SIGUSR1/SIGUSR2 to a footclient instance is currently not
|
You can also send SIGUSR1/SIGUSR2 to a footclient instance, see
|
||||||
supported.
|
*footclient*(1) for details.
|
||||||
|
|
||||||
|
|
||||||
# BUGS
|
# BUGS
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,21 @@ variables may be defined in *foot.ini*(5).
|
||||||
In addition to the variables listed above, custom environment
|
In addition to the variables listed above, custom environment
|
||||||
variables to unset may be defined in *foot.ini*(5).
|
variables to unset may be defined in *foot.ini*(5).
|
||||||
|
|
||||||
|
# Signals
|
||||||
|
|
||||||
|
The following signals have special meaning in footclient:
|
||||||
|
|
||||||
|
- SIGUSR1: switch to color theme 1 (i.e. use the *[colors]* section).
|
||||||
|
- SIGUSR2: switch to color theme 2 (i.e. use the *[colors2]* section).
|
||||||
|
|
||||||
|
When sending SIGUSR1/SIGUSR2 to a footclient instance, the theme is
|
||||||
|
changed in that instance only. This is different from when you send
|
||||||
|
SIGUSR1/SIGUSR2 to the server process, where all instances change the
|
||||||
|
theme.
|
||||||
|
|
||||||
|
Note: for obvious reasons, this is not supported when footclient is
|
||||||
|
started with *--no-wait*.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*foot*(1)
|
*foot*(1)
|
||||||
|
|
|
||||||
59
server.c
59
server.c
|
|
@ -156,10 +156,61 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
||||||
xassert(events & EPOLLIN);
|
xassert(events & EPOLLIN);
|
||||||
|
|
||||||
if (client->instance != NULL) {
|
if (client->instance != NULL) {
|
||||||
uint8_t dummy[128];
|
struct client_ipc_hdr ipc_hdr;
|
||||||
ssize_t count = read(fd, dummy, sizeof(dummy));
|
ssize_t count = read(fd, &ipc_hdr, sizeof(ipc_hdr));
|
||||||
LOG_WARN("client unexpectedly sent %zd bytes", count);
|
|
||||||
return true; /* TODO: shutdown instead? */
|
if (count != sizeof(ipc_hdr)) {
|
||||||
|
LOG_WARN("client unexpectedly sent %zd bytes", count);
|
||||||
|
return true; /* TODO: shutdown instead? */
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ipc_hdr.ipc_code) {
|
||||||
|
case FOOT_IPC_SIGUSR: {
|
||||||
|
xassert(ipc_hdr.size == sizeof(struct client_ipc_sigusr));
|
||||||
|
|
||||||
|
struct client_ipc_sigusr sigusr;
|
||||||
|
count = read(fd, &sigusr, sizeof(sigusr));
|
||||||
|
if (count < 0) {
|
||||||
|
LOG_ERRNO("failed to read SIGUSR IPC data from client");
|
||||||
|
return true; /* TODO: shutdown instead? */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)count != sizeof(sigusr)) {
|
||||||
|
LOG_ERR("failed to read SIGUSR IPC data from client");
|
||||||
|
return true; /* TODO: shutdown instead? */
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sigusr.signo) {
|
||||||
|
case SIGUSR1:
|
||||||
|
term_theme_switch_to_1(client->instance->terminal);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIGUSR2:
|
||||||
|
term_theme_switch_to_2(client->instance->terminal);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_ERR(
|
||||||
|
"client sent bad SIGUSR number: %d "
|
||||||
|
"(expected SIGUSR1=%d or SIGUSR2=%d)",
|
||||||
|
sigusr.signo, SIGUSR1, SIGUSR2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_WARN(
|
||||||
|
"client sent unrecognized IPC (0x%04x), ignoring %hhu bytes",
|
||||||
|
ipc_hdr.ipc_code, ipc_hdr.size);
|
||||||
|
|
||||||
|
/* TODO: slightly broken, since not all data is guaranteed
|
||||||
|
to be readable yet */
|
||||||
|
uint8_t dummy[ipc_hdr.size];
|
||||||
|
read(fd, dummy, ipc_hdr.size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->buffer.data == NULL) {
|
if (client->buffer.data == NULL) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue