diff --git a/doc/alpm-hooks.5.asciidoc b/doc/alpm-hooks.5.asciidoc index 916d43bb..caa55a79 100644 --- a/doc/alpm-hooks.5.asciidoc +++ b/doc/alpm-hooks.5.asciidoc @@ -38,6 +38,12 @@ linkman:pacman.conf[5] (the default is +{sysconfdir}/pacman.d/hooks+). The file names are required to have the suffix ".hook". Hooks are run in alphabetical order of their file name, where the ordering ignores the suffix. +Extra triggers can be added in drop-in directories in the configured hook +directories. Drop-in directories for a hook must be named after the hook with +the ".d" suffix added. For example, "dracut.hook.d" would be the drop-in +directory for "dracut.hook". Trigger files in drop-in directories are required +to have the suffix ".trigger" and can only contain '[Trigger]' sections. + TRIGGERS -------- @@ -96,7 +102,8 @@ OVERRIDING HOOKS Hooks may be overridden by placing a file with the same name in a higher priority hook directory. Hooks may be disabled by overriding them with -a symlink to '/dev/null'. +a symlink to '/dev/null'. The same logic applies to trigger files in drop-in +directories. EXAMPLES -------- diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index aca8707e..19963dab 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -44,6 +44,7 @@ struct _alpm_trigger_t { enum _alpm_hook_op_t op; enum _alpm_trigger_type_t type; alpm_list_t *targets; + char *name; }; struct _alpm_hook_t { @@ -66,6 +67,7 @@ static void _alpm_trigger_free(struct _alpm_trigger_t *trigger) { if(trigger) { FREELIST(trigger->targets); + free(trigger->name); free(trigger); } } @@ -146,16 +148,16 @@ static int _alpm_hook_validate(alpm_handle_t *handle, return ret; } -static int _alpm_hook_parse_cb(const char *file, int line, +#define error(...) _alpm_log(handle, ALPM_LOG_ERROR, __VA_ARGS__); return 1; +#define warning(...) _alpm_log(handle, ALPM_LOG_WARNING, __VA_ARGS__); + +static int _alpm_trigger_parse_cb(const char *file, int line, const char *section, char *key, char *value, void *data) { struct _alpm_hook_cb_ctx *ctx = data; alpm_handle_t *handle = ctx->handle; struct _alpm_hook_t *hook = ctx->hook; -#define error(...) _alpm_log(handle, ALPM_LOG_ERROR, __VA_ARGS__); return 1; -#define warning(...) _alpm_log(handle, ALPM_LOG_WARNING, __VA_ARGS__); - if(!section && !key) { error(_("error while reading hook %s: %s\n"), file, strerror(errno)); } else if(!section) { @@ -166,8 +168,6 @@ static int _alpm_hook_parse_cb(const char *file, int line, struct _alpm_trigger_t *t; CALLOC(t, sizeof(struct _alpm_trigger_t), 1, return 1); hook->triggers = alpm_list_add(hook->triggers, t); - } else if(strcmp(section, "Action") == 0) { - /* no special processing required */ } else { error(_("hook %s line %d: invalid section %s\n"), file, line, section); } @@ -205,6 +205,37 @@ static int _alpm_hook_parse_cb(const char *file, int line, } else { error(_("hook %s line %d: invalid option %s\n"), file, line, key); } + } + + return 0; +} + +static int _alpm_hook_parse_cb(const char *file, int line, + const char *section, char *key, char *value, void *data) +{ + struct _alpm_hook_cb_ctx *ctx = data; + alpm_handle_t *handle = ctx->handle; + struct _alpm_hook_t *hook = ctx->hook; + + if(!section && !key) { + error(_("error while reading hook %s: %s\n"), file, strerror(errno)); + } else if(!section) { + error(_("hook %s line %d: invalid option %s\n"), file, line, key); + } else if(!key) { + /* beginning a new section */ + if(strcmp(section, "Trigger") == 0) { + if (_alpm_trigger_parse_cb(file, line, section, key, value, data) != 0) { + return 1; + } + } else if(strcmp(section, "Action") == 0) { + /* no special processing required */ + } else { + error(_("hook %s line %d: invalid section %s\n"), file, line, section); + } + } else if(strcmp(section, "Trigger") == 0) { + if (_alpm_trigger_parse_cb(file, line, section, key, value, data) != 0) { + return 1; + } } else if(strcmp(section, "Action") == 0) { if(strcmp(key, "When") == 0) { if(hook->when != 0) { @@ -249,12 +280,12 @@ static int _alpm_hook_parse_cb(const char *file, int line, } } -#undef error -#undef warning - return 0; } +#undef error +#undef warning + static int _alpm_hook_trigger_match_file(alpm_handle_t *handle, struct _alpm_hook_t *hook, struct _alpm_trigger_t *t) { @@ -466,6 +497,17 @@ static alpm_list_t *find_hook(alpm_list_t *haystack, const void *needle) return NULL; } +static alpm_list_t *find_trigger(alpm_list_t *haystack, const void *needle) { + while (haystack) { + struct _alpm_trigger_t *t = haystack->data; + if (t && t->name && strcmp(t->name, needle) == 0) { + return haystack; + } + haystack = haystack->next; + } + return NULL; +} + static ssize_t _alpm_hook_feed_targets(char *buf, ssize_t needed, alpm_list_t **pos) { size_t remaining = needed, written = 0;; @@ -529,6 +571,209 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) } } +static int _alpm_hook_parse_drop_in_one(alpm_handle_t *handle, + struct _alpm_hook_t *hook, const char *dir) +{ + char path[PATH_MAX]; + size_t dirlen; + struct dirent *entry; + DIR *d; + size_t suflen = strlen(ALPM_TRIGGER_SUFFIX); + int ret = 0; + + if((dirlen = strlen(dir)) + 1 >= PATH_MAX) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not open drop-in directory: %s: %s\n"), dir, + strerror(ENAMETOOLONG)); + return -1; + } + memcpy(path, dir, dirlen + 1); + + if(!(d = opendir(path))) { + if(errno == ENOENT) { + return 0; + } else { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not open drop-in directory: %s: %s\n"), path, + strerror(errno)); + return -1; + } + } + + while((errno = 0, entry = readdir(d))) { + struct _alpm_hook_cb_ctx ctx = { handle, hook }; + size_t name_len; + struct stat buf; + + if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + if((name_len = strlen(entry->d_name)) >= PATH_MAX - dirlen - 1) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file: %s%s: %s\n"), + path, entry->d_name, strerror(ENAMETOOLONG)); + ret = -1; + continue; + } + + /* Make sure path ends with a '/'. */ + if (dirlen > 0 && path[dirlen - 1] != '/') { + path[dirlen++] = '/'; + } + + memcpy(path + dirlen, entry->d_name, name_len + 1); + + if(name_len < suflen + || strcmp(entry->d_name + name_len - suflen, ALPM_TRIGGER_SUFFIX) != 0) { + _alpm_log(handle, ALPM_LOG_DEBUG, "skipping non-trigger file %s\n", + path); + continue; + } + + if(find_trigger(hook->triggers, entry->d_name)) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "skipping overridden trigger %s\n", path); + continue; + } + + if(stat(path, &buf) != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not stat trigger file %s: %s\n"), path, + strerror(errno)); + ret = -1; + continue; + } + + if(S_ISDIR(buf.st_mode)) { + _alpm_log(handle, ALPM_LOG_DEBUG, "skipping directory %s\n", path); + continue; + } + + _alpm_log(handle, ALPM_LOG_DEBUG, "parsing trigger file %s\n", path); + + if(parse_ini(path, _alpm_trigger_parse_cb, &ctx) != 0 + || _alpm_hook_validate(handle, ctx.hook, path)) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("parsing trigger file %s failed\n"), path); + ret = -1; + continue; + } + + STRDUP(hook->name, entry->d_name, ret = -1); + } + if(errno != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not read directory: %s: %s\n"), + dir, strerror(errno)); + ret = -1; + } + + closedir(d); + + return ret; +} + +static int _alpm_hook_parse_drop_ins(alpm_handle_t *handle, alpm_list_t *hooks) +{ + alpm_list_t *i; + size_t suflen = strlen(ALPM_DROP_IN_SUFFIX); + int ret = 0; + + for(i = alpm_list_last(handle->hookdirs); i; i = alpm_list_previous(i)) { + char path[PATH_MAX]; + size_t dirlen; + struct dirent *entry; + DIR *d; + + if((dirlen = strlen(i->data)) >= PATH_MAX) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file: %s: %s\n"), + (char *)i->data, strerror(ENAMETOOLONG)); + ret = -1; + continue; + } + memcpy(path, i->data, dirlen + 1); + + if(!(d = opendir(path))) { + if(errno == ENOENT) { + continue; + } else { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not open directory: %s: %s\n"), path, + strerror(errno)); + ret = -1; + continue; + } + } + + while((errno = 0, entry = readdir(d))) { + alpm_list_t *j; + struct stat buf; + size_t name_len; + + if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + if((name_len = strlen(entry->d_name)) >= PATH_MAX - dirlen) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file: %s%s: %s\n"), + path, entry->d_name, strerror(ENAMETOOLONG)); + ret = -1; + continue; + } + memcpy(path + dirlen, entry->d_name, name_len + 1); + + if(name_len < suflen + || strcmp(entry->d_name + name_len - suflen, ALPM_DROP_IN_SUFFIX) != 0) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "skipping non-drop-in directory %s\n", path); + continue; + } + + entry->d_name[name_len - 2] = '\0'; + j = find_hook(hooks, entry->d_name); + entry->d_name[name_len - 2] = '.'; + + if (!j) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "skipping drop-in directory %s without a corresponding hook", + path); + continue; + } + + if(stat(path, &buf) != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not stat drop-in directory %s: %s\n"), path, + strerror(errno)); + ret = -1; + continue; + } + + if(!S_ISDIR(buf.st_mode)) { + _alpm_log(handle, ALPM_LOG_DEBUG, "skipping file %s\n", path); + continue; + } + + _alpm_log(handle, ALPM_LOG_DEBUG, "parsing drop-in directory %s\n", path); + + if (_alpm_hook_parse_drop_in_one(handle, j->data, path) != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("failed to read drop-in files in drop-in directory %s"), + path); + ret = -1; + continue; + } + } + if(errno != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not read directory: %s: %s\n"), + (char *) i->data, strerror(errno)); + ret = -1; + } + + closedir(d); + } + + return ret; +} + int _alpm_hook_run(alpm_handle_t *handle, alpm_hook_when_t when) { alpm_event_hook_t event = { .when = when }; @@ -626,6 +871,10 @@ int _alpm_hook_run(alpm_handle_t *handle, alpm_hook_when_t when) closedir(d); } + if (_alpm_hook_parse_drop_ins(handle, hooks) != 0) { + ret = -1; + } + if(ret != 0 && when == ALPM_HOOK_PRE_TRANSACTION) { goto cleanup; } diff --git a/lib/libalpm/hook.h b/lib/libalpm/hook.h index ff5de4f2..961d3238 100644 --- a/lib/libalpm/hook.h +++ b/lib/libalpm/hook.h @@ -23,6 +23,8 @@ #include "alpm.h" #define ALPM_HOOK_SUFFIX ".hook" +#define ALPM_DROP_IN_SUFFIX ".hook.d" +#define ALPM_TRIGGER_SUFFIX ".trigger" int _alpm_hook_run(alpm_handle_t *handle, alpm_hook_when_t when);