[pacman-dev] [PATCH] [RFC] alpm: Add a new hook type: PackageDir
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
On 12/31/15 at 08:38pm, Olivier Brunel wrote:
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
Why is this preferable to just using a File target: [Trigger] Type = File Operation = Install Operation = Upgrade Operation = Remove Target = usr/share/fonts/* [Action] When = PostTransaction Exec = /bin/fc-cache --system-only --force apg
On Thu, 31 Dec 2015 14:54:53 -0500 Andrew Gregory <andrew.gregory.8@gmail.com> wrote:
On 12/31/15 at 08:38pm, Olivier Brunel wrote:
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
Why is this preferable to just using a File target:
[Trigger] Type = File Operation = Install Operation = Upgrade Operation = Remove Target = usr/share/fonts/*
[Action] When = PostTransaction Exec = /bin/fc-cache --system-only --force
apg
Yeah I thought of that, so that was a bad example on my part as for such caches that is fine indeed. But for things like e.g. rebuilding the initramfs, where there might not be such a file criteria, it wouldn't be possible. Or is that just a lone exception (that won't work with hooks) ?
On 01/01/16 06:08, Olivier Brunel wrote:
On Thu, 31 Dec 2015 14:54:53 -0500 Andrew Gregory <andrew.gregory.8@gmail.com> wrote:
On 12/31/15 at 08:38pm, Olivier Brunel wrote:
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
Why is this preferable to just using a File target:
[Trigger] Type = File Operation = Install Operation = Upgrade Operation = Remove Target = usr/share/fonts/*
[Action] When = PostTransaction Exec = /bin/fc-cache --system-only --force
apg
Yeah I thought of that, so that was a bad example on my part as for such caches that is fine indeed. But for things like e.g. rebuilding the initramfs, where there might not be such a file criteria, it wouldn't be possible. Or is that just a lone exception (that won't work with hooks) ?
Can you give a concrete example of something that can not be done with a File hook but could be done with this approach? e.g. for the initramfs example, what would change on the filesystem to cause the need for a rebuild that can not be captured by the File hook? A
On Fri, 1 Jan 2016 09:51:55 +1000 Allan McRae <allan@archlinux.org> wrote:
On 01/01/16 06:08, Olivier Brunel wrote:
On Thu, 31 Dec 2015 14:54:53 -0500 Andrew Gregory <andrew.gregory.8@gmail.com> wrote:
On 12/31/15 at 08:38pm, Olivier Brunel wrote:
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
Why is this preferable to just using a File target:
[Trigger] Type = File Operation = Install Operation = Upgrade Operation = Remove Target = usr/share/fonts/*
[Action] When = PostTransaction Exec = /bin/fc-cache --system-only --force
apg
Yeah I thought of that, so that was a bad example on my part as for such caches that is fine indeed. But for things like e.g. rebuilding the initramfs, where there might not be such a file criteria, it wouldn't be possible. Or is that just a lone exception (that won't work with hooks) ?
Can you give a concrete example of something that can not be done with a File hook but could be done with this approach?
e.g. for the initramfs example, what would change on the filesystem to cause the need for a rebuild that can not be captured by the File hook?
A
Well, basically it seemed to me that Package hooks were really only useful for user-made hooks, not hooks from packages. And, I thought there could be cases where a hook is provided by a package, but would work as a Package hook, only then it's not really making sense, and that's where this PackageDir type would be handy. For example with the initramfs, which is what I had in mind, one could think it'd make sense to rebuild the initramfs when systemd is updated. But why? How do you do your File hook? Only udev-related? Or also other files for those who actually have systemd inside their initramfs? I thought for such cases, going by package name was the way to go/easier. Not to mention it allows the user to add/remove packages from the triggering list. Another example, maybe one uses the encrypt hook (encrypted rootfs) and would like to rebuild the initramfs when crypsetup is updated. So this isn't from a package, but a user, but with a PackageDir it's easy to do: touch /etc/pacman.d/hooks/mkinitcpio.d/cryptsetup -j
participants (3)
-
Allan McRae
-
Andrew Gregory
-
Olivier Brunel