diff options
Diffstat (limited to 'redshift/hooks.c')
-rw-r--r-- | redshift/hooks.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/redshift/hooks.c b/redshift/hooks.c new file mode 100644 index 0000000..96f6f83 --- /dev/null +++ b/redshift/hooks.c @@ -0,0 +1,249 @@ +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun + * + * Copyright (c) 2009-2018 Jon Lund Steffensen <jonlst@gmail.com> + * Copyright (c) 2014-2016, 2025 Mattias Andrée <m@maandree.se> + * + * redshift-ng is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * redshift-ng is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with redshift-ng. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" + + +/** + * Names of periods supplied to scripts + */ +static const char *period_names[] = { + [PERIOD_NONE] = "none", + [PERIOD_DAYTIME] = "daytime", + [PERIOD_NIGHT] = "night", + [PERIOD_TRANSITION] = "transition" +}; + + +/** + * Path name of the hook directory, `NULL` if not found + */ +static char *dirpath = NULL; + +/** + * The allocation size of `dirpath` + */ +static size_t dirpathsize; + +/** + * The length of the string in `dirpath` + */ +static size_t dirpathlen; + + +/** + * Paths, in order of priority, to test when looking for + * the hook directory for redshift + */ +static const struct env_path paths[] = { + {0, "XDG_CONFIG_HOME", "/redshift-ng/hooks"}, + {0, "XDG_CONFIG_HOME", "/redshift/hooks"}, +#if defined(WINDOWS) + {0, "localappdata", "/redshift-ng/hooks"}, + {0, "localappdata", "/redshift/hooks"}, +#endif + {0, "HOME", "/.config/redshift-ng/hooks"}, + {0, "HOME", "/.config/redshift/hooks"}, + {0, NULL, "/.config/redshift-ng/hooks"}, + {0, NULL, "/.config/redshift/hooks"}, + {1, "XDG_CONFIG_DIRS", "/redshift-ng/hooks"}, + {1, "XDG_CONFIG_DIRS", "/redshift/hooks"}, +#if !defined(WINDOWS) + {0, "", "/etc/redshift-ng/hooks"}, + {0, "", "/etc/redshift/hooks"} +#endif +}; + + +/** + * Deallocates `dirpath` + */ +static void +cleanup(void) +{ + free(dirpath); + dirpath = NULL; +} + + +/** + * Search for and open the hook directory for reading + * + * @param path_out Output parameter for the hook directroy path + * @param pathbuf_out Output parameter for the memory allocation for `*path_out`; + * will be set to `NULL`; shall be free(3)d by the caller + * @return `DIR` object for the reading the directory; `NULL` if not found + */ +static DIR * +open_hooks_dir(const char **path_out, char **pathbuf_out) +{ + DIR *dir = NULL; + size_t i; + + *path_out = NULL; + *pathbuf_out = NULL; + + for (i = 0; !dir && i < ELEMSOF(paths); i++) + dir = try_path_opendir(&paths[i], path_out, pathbuf_out); + if (dir) + weprintf(_("Found hook directory `%s'."), *path_out); + else + weprintf(_("No hook directory found.")); + + return dir; +} + + +/** + * Run hook file + * + * @param path The path to the hook file + * @param argv `NULL` terminated list of command line arguments + * for the hooks; must contain an unused initial slot, + * which the function will use to provide the zeroth + * argument + */ +static void +run_hook(const char *path, const char *argv[]) +{ +#ifdef WINDOWS + /* TODO [Windows] hooks are not support on Windows */ +#else + switch (fork()) { + case -1: + weprintf("fork:"); + break; + + case 0: + if (dup2(STDOUT_FILENO, STDERR_FILENO) != STDERR_FILENO) { + weprintf("dup2 <stdout> <stderr>:"); + _exit(1); + } + argv[0] = path; + execv(path, (const void *)argv); + if (errno != EACCES) + weprintf("execv %s:", path); + _exit(1); + + default: + /* SIGCHLD is ignored */ + break; + } +#endif +} + + +/** + * Run hooks + * + * @param argv `NULL` terminated list of command line arguments + * for the hooks; must contain an unused initial slot, + * which the function will use to provide the zeroth + * argument + */ +static void +run_hooks(const char *argv[]) +{ + static int looked_up_dir = 0; + static int hook_file_is_regular = 0; + static int hook_file_checked = 0; + + DIR *dir; + struct dirent *f; + size_t required; + const char *dirpath_static; + + if (hook_file_is_regular) { + goto run_hook_file; + + } else if (hook_file) { + if (!hook_file_checked) { + hook_file_checked = 1; + if (!strcmp(hook_file, "/dev/null") || + !strcmp(hook_file, "/var/empty") || + !strcmp(hook_file, "/var/empty/")) + goto no_hooks; + } + + dir = opendir(hook_file); + if (!dir) { + if (errno == ENOTDIR) { + hook_file_is_regular = 1; + run_hook_file: + run_hook(hook_file, argv); + return; + } + weprintf("opendir %s:", hook_file); + no_hooks: + free(hook_file); + hook_file = NULL; + looked_up_dir = 1; + dirpath = NULL; + return; + } + + } else if (!looked_up_dir) { + looked_up_dir = 1; + dir = open_hooks_dir(&dirpath_static, &dirpath); + if (!dir) + return; + if (!dirpath) + dirpath = estrdup(dirpath_static); + dirpathsize = dirpathlen = strlen(dirpath); + atexit(&cleanup); + + } else if (dirpath) { + dir = opendir(dirpath); + if (!dir) { + weprintf("opendir %s:", dirpath); + cleanup(); + return; + } + + } else { + return; + } + + while ((errno = 0, f = readdir(dir))) { + if (f->d_name[0] == '.' || !f->d_name[0] || strchr(f->d_name, '\0')[-1] == '~') + continue; + + required = dirpathlen + sizeof("/") + strlen(f->d_name); + if (required > dirpathsize) + dirpath = erealloc(dirpath, dirpathsize = required); + stpcpy(stpcpy(&dirpath[dirpathlen], "/"), f->d_name); + + run_hook(dirpath, argv); + + dirpath[dirpathlen] = '\0'; + } + + if (errno) + weprintf("readdir %s:", dirpath); + + closedir(dir); +} + + +void +run_period_change_hooks(enum period prev_period, enum period period) +{ + const char *argv[] = {NULL, "period-changed", period_names[prev_period], period_names[period], NULL}; + run_hooks(argv); +} |