aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2025-03-24 16:34:15 +0100
committerMattias Andrée <m@maandree.se>2025-03-24 16:34:15 +0100
commit11f89cf7a9a7375b74a239311e394db3c952fbd9 (patch)
tree8cdd850a991dd9fcac9ad663454750427425edd7 /src
parentCleanup (diff)
downloadredshift-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>
Diffstat (limited to '')
-rw-r--r--src/common.h35
-rw-r--r--src/config-ini.c14
-rw-r--r--src/config.c83
-rw-r--r--src/hooks.c101
-rw-r--r--src/redshift.c1
5 files changed, 196 insertions, 38 deletions
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;
}