Same as Package only instead of defining matching packages in the .hook file, it is done on the filesystem. Signed-off-by: Olivier Brunel <jjk@jjacky.com> --- Hey there, So I may be totally wrong, but I thought one of the cool things that could be done with hooks was that instead of having e.g. 3 packages refreshing a cache in their post_upgrade scriptlets, it'll be done via one post-transaction hook, so that it only runs once. And unless I'm missing something, this doesn't seem possible currently. At least not in a "good" way, since all matching packages must be included inside the .hook file itself. So am I wrong and was this never planned? Or is actually possible? Or is it meant to be added later? In case, here's a patch (though no documentation) that adds a new type of hooks: PackageDir Those are just like Package (and in fact turned into Package once validated/filled), only no targets should be featured inside the hook file itself, instead alpm will check for a directory by the hook name plus ".d" inside all the hookdirs (in order), so e.g. for hook "foobar" folders "foobar.d" will be looked for. Inside those dirs, a regular file means a target to add, said target being the name of the file. So files must be named after the packages to add/trigger the hook. A symlink however will mean to remove said package from the targets; The idea being that a user could create a symlink (to /dev/null) in e.g. /etc/pacman.d/hooks/foobar.d by the name of a package, to "undo"/mask a file by the same name in /usr/lib/alpm/hooks/foobar.d, file that could be part of any (other) package. That way, the font package provides the hook font-cache and any package that needs to trigger it simply adds an empty file to /usr/lib/alpm/hooks/font-cache.d by the name of the package. Also works for mkinitcpio providing a hook to rebuild the initramfs, which can then be triggered by any of e.g. mkinitcpio, linux, systemd, etc Thoughts? -j lib/libalpm/hook.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index b5ed17d..876beaf 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -37,6 +37,7 @@ enum _alpm_hook_op_t { enum _alpm_trigger_type_t { ALPM_HOOK_TYPE_PACKAGE = 1, + ALPM_HOOK_TYPE_PACKAGE_DIR, ALPM_HOOK_TYPE_FILE, }; @@ -95,12 +96,97 @@ static void _alpm_hook_free(struct _alpm_hook_t *hook) } } +static int _alpm_trigger_load_targets(alpm_handle_t *handle, + struct _alpm_trigger_t *trigger, char *hookname) +{ + int ret = 0; + size_t len = strlen(hookname); + alpm_list_t *i; + + for(i = handle->hookdirs; i && ret == 0; i = alpm_list_next(i)) { + int err; + char path[PATH_MAX]; + size_t dirlen = strlen(i->data); + struct dirent entry, *result; + DIR *d; + + if(dirlen + len + 3 >= PATH_MAX) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not open directory: %s: %s\n"), + (char *)i->data, strerror(ENAMETOOLONG)); + ret = -1; + break; + } + memcpy(path, i->data, dirlen); + memcpy(path + dirlen, hookname, len); + memcpy(path + dirlen + len, ".d/", 4); + + 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; + break; + } + } + + while((err = readdir_r(d, &entry, &result)) == 0 && result) { + struct stat buf; + + if(strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) { + continue; + } + + if(fstatat(dirfd(d), entry.d_name, &buf, 0) != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not stat file %s%s: %s\n"), path, entry.d_name, strerror(errno)); + ret = -1; + break; + } + + if(S_ISREG(buf.st_mode)) { + if(!alpm_list_find_str(trigger->targets, entry.d_name)) { + char *s; + STRDUP(s, entry.d_name, ret = -1; break); + trigger->targets = alpm_list_add(trigger->targets, s); + } + } else if(S_ISLNK(buf.st_mode)) { + char *s; + trigger->targets = alpm_list_remove_str(trigger->targets, entry.d_name, &s); + free(s); + } + } + + if(err != 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; +} + static int _alpm_trigger_validate(alpm_handle_t *handle, - struct _alpm_trigger_t *trigger, const char *file) + struct _alpm_trigger_t *trigger, const char *file, char *hookname) { int ret = 0; - if(trigger->targets == NULL) { + if(trigger->type == ALPM_HOOK_TYPE_PACKAGE_DIR) { + trigger->type = ALPM_HOOK_TYPE_PACKAGE; + if(trigger->targets) { + ret = -1; + _alpm_log(handle, ALPM_LOG_ERROR, + _("Unexpected targets for PackageDir trigger in hook: %s\n"), file); + } else if(_alpm_trigger_load_targets(handle, trigger, hookname) < 0) { + ret = -1; + } + /* we allow no targets in this case, since e.g. they could have been + * "masked" by the user via symlinks */ + } else if(trigger->targets == NULL) { ret = -1; _alpm_log(handle, ALPM_LOG_ERROR, _("Missing trigger targets in hook: %s\n"), file); @@ -134,7 +220,7 @@ static int _alpm_hook_validate(alpm_handle_t *handle, } for(i = hook->triggers; i; i = i->next) { - if(_alpm_trigger_validate(handle, i->data, file) != 0) { + if(_alpm_trigger_validate(handle, i->data, file, hook->name) != 0) { ret = -1; } } @@ -297,6 +383,8 @@ static int _alpm_hook_parse_cb(const char *file, int line, } else if(strcmp(key, "Type") == 0) { if(strcmp(value, "Package") == 0) { t->type = ALPM_HOOK_TYPE_PACKAGE; + } else if(strcmp(value, "PackageDir") == 0) { + t->type = ALPM_HOOK_TYPE_PACKAGE_DIR; } else if(strcmp(value, "File") == 0) { t->type = ALPM_HOOK_TYPE_FILE; } else { -- 2.6.4