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(a)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