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
|
||||
newly spawned client instances to use the selected theme, instead of
|
||||
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
|
||||
|
|
|
|||
|
|
@ -29,3 +29,17 @@ struct client_data {
|
|||
} __attribute__((packed));
|
||||
|
||||
_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;
|
||||
|
||||
static volatile sig_atomic_t aborted = 0;
|
||||
static volatile sig_atomic_t sigusr = 0;
|
||||
|
||||
static void
|
||||
sig_handler(int signo)
|
||||
sigint_handler(int signo)
|
||||
{
|
||||
aborted = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
sigusr_handler(int signo)
|
||||
{
|
||||
sigusr = signo;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
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))
|
||||
goto err;
|
||||
|
||||
struct sigaction sa = {.sa_handler = &sig_handler};
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0) {
|
||||
struct sigaction sa_int = {.sa_handler = &sigint_handler};
|
||||
struct sigaction sa_usr = {.sa_handler = &sigusr_handler};
|
||||
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");
|
||||
goto err;
|
||||
}
|
||||
|
||||
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)
|
||||
xassert(aborted);
|
||||
|
|
|
|||
|
|
@ -694,14 +694,14 @@ variables to unset may be defined in *foot.ini*(5).
|
|||
The following signals have special meaning in foot:
|
||||
|
||||
- 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,
|
||||
in which case all client instances will switch theme. Furthermore, all
|
||||
future client instances will also use the selected theme.
|
||||
|
||||
Sending SIGUSR1/SIGUSR2 to a footclient instance is currently not
|
||||
supported.
|
||||
You can also send SIGUSR1/SIGUSR2 to a footclient instance, see
|
||||
*footclient*(1) for details.
|
||||
|
||||
|
||||
# BUGS
|
||||
|
|
|
|||
|
|
@ -189,6 +189,21 @@ variables may be defined in *foot.ini*(5).
|
|||
In addition to the variables listed above, custom environment
|
||||
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
|
||||
|
||||
*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);
|
||||
|
||||
if (client->instance != NULL) {
|
||||
uint8_t dummy[128];
|
||||
ssize_t count = read(fd, dummy, sizeof(dummy));
|
||||
LOG_WARN("client unexpectedly sent %zd bytes", count);
|
||||
return true; /* TODO: shutdown instead? */
|
||||
struct client_ipc_hdr ipc_hdr;
|
||||
ssize_t count = read(fd, &ipc_hdr, sizeof(ipc_hdr));
|
||||
|
||||
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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue