1
0
mirror of https://github.com/emersion/kanshi synced 2024-09-18 09:51:36 +02:00

Add signal handlers to reload the config.

Signaling kanshi with SIGHUP will now reload the config.

Co-authored-by: Érico Nogueira <erico.erc@gmail.com>
This commit is contained in:
Jason Francis 2021-07-07 00:00:51 -03:00 committed by Simon Ser
parent bec47f49e3
commit 123a2f43f3
4 changed files with 135 additions and 0 deletions

View File

@ -10,15 +10,70 @@
#include "kanshi.h"
static int set_pipe_flags(int fd) {
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
perror("fnctl F_GETFL failed");
return -1;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
perror("fnctl F_SETFL failed");
return -1;
}
flags = fcntl(fd, F_GETFD);
if (flags == -1) {
perror("fnctl F_GETFD failed");
return -1;
}
flags |= O_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
perror("fnctl F_SETFD failed");
return -1;
}
return 0;
}
static int signal_pipefds[2];
static void signal_handler(int signum) {
if (write(signal_pipefds[1], &signum, sizeof(signum)) == -1) {
abort();
}
}
enum readfds_type {
FD_WAYLAND,
FD_SIGNAL,
FD_COUNT,
};
int kanshi_main_loop(struct kanshi_state *state) {
if (pipe(signal_pipefds) == -1) {
perror("read from signalfd failed");
return EXIT_FAILURE;
}
if (set_pipe_flags(signal_pipefds[0]) == -1) {
return EXIT_FAILURE;
}
if (set_pipe_flags(signal_pipefds[1]) == -1) {
return EXIT_FAILURE;
}
struct sigaction action;
sigfillset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = signal_handler;
sigaction(SIGINT, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGHUP, &action, NULL);
struct pollfd readfds[FD_COUNT] = {0};
readfds[FD_WAYLAND].fd = wl_display_get_fd(state->display);
readfds[FD_WAYLAND].events = POLLIN;
readfds[FD_SIGNAL].fd = signal_pipefds[0];
readfds[FD_SIGNAL].events = POLLIN;
while (state->running) {
while (wl_display_prepare_read(state->display) != 0) {
@ -52,6 +107,36 @@ int kanshi_main_loop(struct kanshi_state *state) {
return EXIT_FAILURE;
}
if (readfds[FD_SIGNAL].revents & POLLIN) {
for (;;) {
int signum;
ssize_t s
= read(readfds[FD_SIGNAL].fd, &signum, sizeof(signum));
if (s == 0) {
break;
}
if (s < 0) {
if (errno == EAGAIN) {
break;
}
perror("read from signal pipe failed");
return EXIT_FAILURE;
}
if (s < (ssize_t) sizeof(signum)) {
fprintf(stderr, "read too few bytes from signal pipe\n");
return EXIT_FAILURE;
}
switch (signum) {
case SIGHUP:
kanshi_reload_config(state);
break;
default:
/* exiting after signal considered successful */
return EXIT_SUCCESS;
}
}
}
if (wl_display_dispatch_pending(state->display) == -1) {
return EXIT_FAILURE;
}

View File

@ -45,6 +45,7 @@ struct kanshi_state {
struct zwlr_output_manager_v1 *output_manager;
struct kanshi_config *config;
const char *config_arg;
struct wl_list heads;
uint32_t serial;
@ -57,6 +58,8 @@ struct kanshi_pending_profile {
struct kanshi_profile *profile;
};
bool kanshi_reload_config(struct kanshi_state *state);
int kanshi_main_loop(struct kanshi_state *state);
#endif

View File

@ -24,6 +24,8 @@ kanshi is configured with a list of output profiles. Each profile contains a set
of outputs. A profile will be automatically activated if all specified outputs
are currently connected. A profile contains configuration for each output.
If kanshi receives a SIGHUP signal, it will reread its config file.
# CONFIGURATION
kanshi reads its configuration from *$XDG_CONFIG_HOME/kanshi/config*. If unset,

45
main.c
View File

@ -87,6 +87,16 @@ static void exec_command(char *cmd) {
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
struct sigaction action;
sigfillset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = SIG_DFL;
sigaction(SIGINT, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGHUP, &action, NULL);
if ((grandchild = fork()) == 0) {
execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
fprintf(stderr, "Executing command '%s' failed: %s\n", cmd, strerror(errno));
@ -487,6 +497,40 @@ static struct kanshi_config *read_config(const char *config) {
return parse_config(config_path);
}
static void destroy_config(struct kanshi_config *config) {
struct kanshi_profile *profile, *tmp_profile;
wl_list_for_each_safe(profile, tmp_profile, &config->profiles, link) {
struct kanshi_profile_output *output, *tmp_output;
wl_list_for_each_safe(output, tmp_output, &profile->outputs, link) {
free(output->name);
wl_list_remove(&output->link);
free(output);
}
struct kanshi_profile_command *command, *tmp_command;
wl_list_for_each_safe(command, tmp_command, &profile->commands, link) {
free(command->command);
wl_list_remove(&command->link);
free(command);
}
wl_list_remove(&profile->link);
free(profile);
}
free(config);
}
bool kanshi_reload_config(struct kanshi_state *state) {
fprintf(stderr, "reloading config\n");
struct kanshi_config *config = read_config(state->config_arg);
if (config != NULL) {
destroy_config(state->config);
state->config = config;
state->pending_profile = NULL;
state->current_profile = NULL;
return try_apply_profiles(state);
}
return false;
}
static const char usage[] = "Usage: %s [options...]\n"
" -h, --help Show help message and quit\n"
" -c, --config <path> Path to config file.\n";
@ -530,6 +574,7 @@ int main(int argc, char *argv[]) {
.running = true,
.display = display,
.config = config,
.config_arg = config_arg,
};
int ret = EXIT_SUCCESS;
wl_list_init(&state.heads);