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:
parent
bec47f49e3
commit
123a2f43f3
85
event-loop.c
85
event-loop.c
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
45
main.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user