diff options
author | Mattias Andrée <m@maandree.se> | 2025-03-24 16:34:15 +0100 |
---|---|---|
committer | Mattias Andrée <m@maandree.se> | 2025-03-24 16:34:15 +0100 |
commit | 11f89cf7a9a7375b74a239311e394db3c952fbd9 (patch) | |
tree | 8cdd850a991dd9fcac9ad663454750427425edd7 | |
parent | Cleanup (diff) | |
download | redshift-ng-11f89cf7a9a7375b74a239311e394db3c952fbd9.tar.gz redshift-ng-11f89cf7a9a7375b74a239311e394db3c952fbd9.tar.bz2 redshift-ng-11f89cf7a9a7375b74a239311e394db3c952fbd9.tar.xz |
Add support for specifying hook file
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r-- | README | 15 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | redshift.1 | 19 | ||||
-rw-r--r-- | src/common.h | 35 | ||||
-rw-r--r-- | src/config-ini.c | 14 | ||||
-rw-r--r-- | src/config.c | 83 | ||||
-rw-r--r-- | src/hooks.c | 101 | ||||
-rw-r--r-- | src/redshift.c | 1 |
8 files changed, 230 insertions, 41 deletions
@@ -9,7 +9,7 @@ NAME SYNOPSIS redshift [-b brightness] [-c config-file] [-D | +D] - [-E | +E | -e elevations] [-g gamma] + [-E | +E | -e elevations] [-g gamma] [-H hook-file] [-l latitude:longitude | -l provider[:options]] [-m method[:options]] [-P | +P] [-r | +r] [-dv] [-O temperature | -o | -p | -t temperature | -x] | -h | -V @@ -123,6 +123,12 @@ OPTIONS nothing is omitted or an entire set, including its two colons (:) are omitted. + -H hook-file + Select hook file or directory. + + /dev/null or /var/empty can be used to tell redshift not to run + hook files. + -h Display help message. @@ -531,6 +537,13 @@ EXTENDED DESCRIPTION however if only one value is specified it is applied to all each channels. + hook = file or directory + Set hook file or directory. If not specified, the default + paths are searched. + + /dev/null and /var/empty can be used to prevent redshift from + executing hooks. + fade = 0 or 1 Disable (if 0) or enable (if 1) fading between colour settings with large differences. @@ -93,15 +93,14 @@ https://github.com/jonls/redshift/issues/31 Change temperature from the icon https://github.com/jonls/redshift/issues/629 add option to switch between profiles https://github.com/jonls/redshift/issues/347 Support Wingpanel API -Add -H for hook file/directory, support /var/empty on all platforms (should always disable hooks even if that directory isn't actually empty) po/sv.po: lost -> förlorad -> försvunnen po/de.po: lost -> verloren -> verschwunden po/sv.po: felaktig -> ogiltig (?) Optionally use METAR data adjust day-time colour temperature according the cloudiness read the monitor's chromas to determine it's colour space and correct the RGB multipliers according -Use coordinates (zone1970.tab, be aware that the coordinates are store in degress and minutes, and sometimes also seconds, not degrees with decimals), for the user's timezone, from tzdata as a fallback Add support for solar time. When disabled, redshift should be periodically set the gamma ramps Add signals for setting location Add signals for responding with sigqueue the state of redshift print, for use by frontends, what mode redshift was started in +[Windows] Make sure extended paths are supported @@ -12,6 +12,8 @@ redshift \- Automatically adjust display colour temperature according the Sun .IR elevations ] [-g .IR gamma ] +[-H +.IR hook-file ] [-l .IB latitude : longitude | -l @@ -179,6 +181,14 @@ nothing is omitted or an entire set, including its two colons .RB ( : ) are omitted. .TP +.BI -H\fR\ hook-file +Select hook file or directory. + +.B /dev/null +or +.B /var/empty +can be used to tell redshift not to run hook files. +.TP .B -h Display help message. .TP @@ -840,6 +850,15 @@ Set gamma correction for nighttime. Values must be within [0.1, however if only one value is specified it is applied to all each channels. .TP +.BI hook\fR\ =\ "file or directory" +Set hook file or directory. If not specified, the default +paths are searched. + +.B /dev/null +and +.B /var/empty +can be used to prevent redshift from executing hooks. +.TP .BI fade\fR\ =\ \fP0 " or " 1 Disable (if .BR 0 ) diff --git a/src/common.h b/src/common.h index f5be542..61e5c79 100644 --- a/src/common.h +++ b/src/common.h @@ -738,6 +738,14 @@ struct setting_time { }; /** + * `char *` valued setting with setting source + */ +struct setting_str { + enum setting_source source; /**< Setting source */ + char *value; /**< Setting value */ +}; + +/** * Intermediate settings representation of colour settings * (for a non-transitional period) with settings sources * used for determining whether settings from the configuration @@ -807,6 +815,14 @@ struct settings { int until_death; /** + * The path to the hook file or hook directory, `NULL` + * if unspecified (search default paths) + * + * This represents the "hook" setting and "-H" option + */ + struct setting_str hook_file; + + /** * Whether the program should preserve preapplied * colour calibrations * @@ -1183,6 +1199,12 @@ extern int use_fade; */ extern int verbose; +/** + * The path to the hook file or hook directory, `NULL` + * if unspecified (search default paths) + */ +extern char *hook_file; + /* backend-direct.c */ @@ -1357,11 +1379,14 @@ LIST_RAMPS_STOP_VALUE_TYPES(X, ;); /** * Load the configuration file * - * @param state Output parameter for the configurations - * @param path The path to the configuration file, or `NULL` if the - * application should look for it in the default paths - */ -void config_ini_init(struct config_ini_state *state, const char *path); + * @param state Output parameter for the configurations + * @param path The path to the configuration file, or `NULL` if the + * application should look for it in the default paths + * @param pathbuf_out Output parameter for the memory allocated for the + * return value + * @return The path to the loaded configuration file, `NULL` if none + */ +const char *config_ini_init(struct config_ini_state *state, const char *path, char **pathbuf_out); /** * Deallocate the settings loaded diff --git a/src/config-ini.c b/src/config-ini.c index d5056dc..13aaa57 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -146,12 +146,12 @@ open_config_file(const char *path, const char **path_out, char **pathbuf_out, in } -void -config_ini_init(struct config_ini_state *state, const char *path) +const char * +config_ini_init(struct config_ini_state *state, const char *path, char **pathbuf_out) { struct config_ini_section *section = NULL; struct config_ini_setting *setting; - char *line = NULL, *s, *p, *value, *end, *pathbuf; + char *line = NULL, *s, *p, *value, *end; char *next_line = NULL, *name; size_t size = 0; ssize_t len = 0; /* initialised to silence false warning from clang */ @@ -159,10 +159,11 @@ config_ini_init(struct config_ini_state *state, const char *path) int should_close; state->sections = NULL; + *pathbuf_out = NULL; - f = open_config_file(path, &path, &pathbuf, &should_close); + f = open_config_file(path, &path, pathbuf_out, &should_close); if (!f) - return; + return NULL; #ifndef WINDOWS again: @@ -227,10 +228,11 @@ again: eprintf("getline %s:", path); } - free(pathbuf); free(line); if (should_close) fclose(f); + + return path; } diff --git a/src/config.c b/src/config.c index 7843c08..29c5a53 100644 --- a/src/config.c +++ b/src/config.c @@ -30,7 +30,7 @@ usage_no_exit(FILE *f) { fprintf(f, _("Usage: %s %s\n"), argv0, _("[-b brightness] [-c config-file] [-D | +D] [-E | +E | -e elevations] " - "[-g gamma] [-l latitude:longitude | -l provider[:options]] " + "[-g gamma] [-H hook-file] [-l latitude:longitude | -l provider[:options]] " "[-m method[:options]] [-P | +P] [-r | +r] [-dv] " "[-O temperature | -o | -p | -t temperature | -x] | -h | -V")); } @@ -54,6 +54,7 @@ enum program_mode mode = PROGRAM_MODE_CONTINUAL; int preserve_gamma; int use_fade; int verbose = 0; +char *hook_file; /** @@ -78,6 +79,7 @@ print_help(void) printf(_(" -e day:night Select solar elevation thresholds for day and night (Default: %g:%g)\n"), DEFAULT_HIGH_ELEVATION, DEFAULT_LOW_ELEVATION); printf(_(" -g day:night Additional gamma correction (Default: 1:1:1:1:1:1)\n")); + printf(_(" -H hook-file Select hook file or directrory\n")); printf(_(" -h Display this help message\n")); printf(_(" -l lat:lon Specific geographical location\n")); printf(_(" -l provider[:options] Select location provider to get geographical location\n")); @@ -657,6 +659,7 @@ load_defaults(struct settings *settings) settings->night.gamma.value[1] = DEFAULT_NIGHT_GAMMA; settings->night.gamma.value[2] = DEFAULT_NIGHT_GAMMA; + settings->hook_file.value = NULL; settings->preserve_gamma.value = 1; settings->use_fade.value = 1; @@ -716,6 +719,12 @@ load_from_cmdline(struct settings *settings, int argc, char *argv[]) set_gamma(ARG(), &settings->day.gamma, &settings->night.gamma, NULL); break; + case 'H': + settings->hook_file.source |= SETTING_CMDLINE; + free(settings->hook_file.value); + settings->hook_file.value = ARG(); + break; + case 'h': print_help(); exit(0); @@ -899,6 +908,15 @@ load_from_config_ini(struct settings *settings, const char *key, char *value) if (settings->use_fade.source <= SETTING_CONFIGFILE) settings->use_fade.value = get_boolean(value, key); + } else if (!strcasecmp(key, "hook")) { + if (settings->hook_file.source & SETTING_CONFIGFILE) + weprintf(_("`hook' setting specified multiple times in configuration file.")); + settings->hook_file.source |= SETTING_CONFIGFILE; + if (settings->hook_file.source <= SETTING_CONFIGFILE) { + free(settings->hook_file.value); + settings->hook_file.value = estrdup(value); + } + } else if (!strcasecmp(key, "preserve-gamma")) { if (settings->preserve_gamma.source & SETTING_CONFIGFILE) weprintf(_("`preserve-gamma' setting specified multiple times in configuration file.")); @@ -955,8 +973,11 @@ load_settings(struct settings *settings, int argc, char *argv[]) struct config_ini_section *section; struct config_ini_setting *setting; const struct time_period *first, *current; + const char *conf_path, *p; + char *conf_pathbuf, *s; int i, j, n; time_t duration; + size_t len; /* Clear unused bit so they do not interfere with comparsion */ memset(&day_settings, 0, sizeof(day_settings)); @@ -965,7 +986,7 @@ load_settings(struct settings *settings, int argc, char *argv[]) /* Load settings; some validation takes place */ load_defaults(settings); load_from_cmdline(settings, argc, argv); - config_ini_init(&settings->config, settings->config_file); + conf_path = config_ini_init(&settings->config, settings->config_file, &conf_pathbuf); if ((section = config_ini_get_section(&settings->config, "redshift"))) for (setting = section->settings; setting; setting = setting->next) load_from_config_ini(settings, setting->name, setting->value); @@ -1014,9 +1035,67 @@ load_settings(struct settings *settings, int argc, char *argv[]) goto settings_published; } + /* Make hook file absolute if set from config file (relative to config file) */ + if (settings->hook_file.source == SETTING_CONFIGFILE && conf_path) { +#ifdef WINDOWS + /* Regular absolute path */ + if (isalpha(settings->hook_file.value[0]) && settings->hook_file.value[1] == ':') + goto absolute_hook_path; + /* Absolute extended path or network path (relative extended paths do not exist) */ + if (settings->hook_file.value[0] == '\\' && settings->hook_file.value[1] == '\\') + goto absolute_hook_path; + /* Path relative to root */ + if (settings->hook_file.value[0] == '\\' || settings->hook_file.value[0] == '/') { + /* This is safe as we know that `conf_path` is a valid path */ + if (isalpha(conf_path[0]) && conf_path[1] == ':') { + p = &conf_path[3]; + goto base_found; + } else if (conf_path[0] == '\\' && conf_path[1] == '\\') { + p = &conf_path[2]; + while (*p == '\\') + p++; + while (*p != '/' && *p != '\\') + p++; + goto base_found; + } + } +#else + if (settings->hook_file.value[0] == '/') + goto absolute_hook_path; +#endif + p = strrchr(conf_path, '/'); + p = p ? &p[1] : conf_path; +#ifdef WINDOWS + if (strrchr(p, '\\')) + p = &strrchr(p, '\\')[1]; + base_found: +#endif + len = (size_t)(p - conf_path); + s = emalloc(len + strlen(settings->hook_file.value) + 1U); + memcpy(s, conf_path, len); + stpcpy(&s[len], settings->hook_file.value); + free(settings->hook_file.value); + settings->hook_file.value = s; +#ifdef WINDOWS + /* Used extended path is too long */ + if (settings->hook_file.value[0] != '\\' && (len = strlen(settings->hook_file.value)) >= 260) { + /* We have already made sure the path is absolute, so \ prefix is always extended or network path */ + settings->hook_file.value = erealloc(settings->hook_file.value, len + sizeof("\\\\?\\")); + memmove(&settings->hook_file.value[4], &settings->hook_file.value, len + 1U); + settings->hook_file.value[0] = '\\'; + settings->hook_file.value[1] = '\\'; + settings->hook_file.value[2] = '?'; + settings->hook_file.value[3] = '\\'; + } +#endif + } + free(conf_pathbuf); +absolute_hook_path: + /* Publish loaded settings */ if (mode == PROGRAM_MODE_ONE_SHOT && settings->until_death) mode = PROGRAM_MODE_UNTIL_DEATH; + hook_file = settings->hook_file.value, settings->hook_file.value = NULL; preserve_gamma = settings->preserve_gamma.value; use_fade = settings->use_fade.value; disable ^= settings->disabled.value; diff --git a/src/hooks.c b/src/hooks.c index be02b18..96f6f83 100644 --- a/src/hooks.c +++ b/src/hooks.c @@ -111,6 +111,45 @@ open_hooks_dir(const char **path_out, char **pathbuf_out) /** + * 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 @@ -122,13 +161,44 @@ 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 (!looked_up_dir) { + 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) @@ -137,6 +207,7 @@ run_hooks(const char *argv[]) dirpath = estrdup(dirpath_static); dirpathsize = dirpathlen = strlen(dirpath); atexit(&cleanup); + } else if (dirpath) { dir = opendir(dirpath); if (!dir) { @@ -144,6 +215,7 @@ run_hooks(const char *argv[]) cleanup(); return; } + } else { return; } @@ -155,32 +227,11 @@ run_hooks(const char *argv[]) required = dirpathlen + sizeof("/") + strlen(f->d_name); if (required > dirpathsize) dirpath = erealloc(dirpath, dirpathsize = required); + stpcpy(stpcpy(&dirpath[dirpathlen], "/"), f->d_name); -#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); - } - stpcpy(stpcpy(&dirpath[dirpathlen], "/"), f->d_name); - argv[0] = dirpath; - execv(dirpath, (const void *)argv); - if (errno != EACCES) - weprintf("execv %s:", dirpath); - _exit(1); + run_hook(dirpath, argv); - default: - /* SIGCHLD is ignored */ - break; - } -#endif + dirpath[dirpathlen] = '\0'; } if (errno) diff --git a/src/redshift.c b/src/redshift.c index bcd9db9..5bc172e 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -538,5 +538,6 @@ out: provider->free(provider_state); if (method_state) method->free(method_state); + free(hook_file); return 0; } |