[pacman-dev] [PATCH 1/6] order hooks by file name
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- lib/libalpm/hook.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index fe4b204..c7b4ee8 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -363,6 +363,11 @@ static int _alpm_hook_triggered(alpm_handle_t *handle, struct _alpm_hook_t *hook return 0; } +static int _alpm_hook_cmp(struct _alpm_hook_t *h1, struct _alpm_hook_t *h2) +{ + return strcmp(h1->name, h2->name); +} + static alpm_list_t *find_hook(alpm_list_t *haystack, const void *needle) { while(haystack) { @@ -478,6 +483,9 @@ int _alpm_hook_run(alpm_handle_t *handle, enum _alpm_hook_when_t when) closedir(d); } + hooks = alpm_list_msort(hooks, alpm_list_count(hooks), + (alpm_list_fn_cmp)_alpm_hook_cmp); + for(i = hooks; i; i = i->next) { struct _alpm_hook_t *hook = i->data; if(hook && hook->when == when && _alpm_hook_triggered(handle, hook)) { -- 2.6.2
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- lib/libalpm/hook.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index c7b4ee8..463b76a 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -210,7 +210,7 @@ static int _alpm_hook_parse_cb(const char *file, int line, } else if(strcmp(key, "Exec") == 0) { STRDUP(hook->cmd, value, return 1); } else { - error(_("hook %s line %d: invalid option %s\n"), file, line, value); + error(_("hook %s line %d: invalid option %s\n"), file, line, key); } } -- 2.6.2
On 27/10/15 14:47, Andrew Gregory wrote:
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- lib/libalpm/hook.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
OK.
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- Updated caveats section. doc/.gitignore | 1 + doc/Makefile.am | 4 ++ doc/alpm-hooks.5.txt | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 doc/alpm-hooks.5.txt diff --git a/doc/.gitignore b/doc/.gitignore index ad496ce..2eae9e4 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,3 +1,4 @@ +alpm-hooks.5 PKGBUILD.5 libalpm.3 makepkg.8 diff --git a/doc/Makefile.am b/doc/Makefile.am index 60a70b3..90d90f3 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -4,6 +4,7 @@ # man_MANS if --enable-asciidoc and/or --enable-doxygen are used. ASCIIDOC_MANS = \ + alpm-hooks.5 \ pacman.8 \ makepkg.8 \ makepkg-template.1 \ @@ -20,6 +21,7 @@ ASCIIDOC_MANS = \ DOXYGEN_MANS = $(wildcard man3/*.3) HTML_MANPAGES = \ + alpm-hooks.5.html \ pacman.8.html \ makepkg.8.html \ makepkg-template.1.html \ @@ -46,6 +48,7 @@ HTML_DOCS = \ EXTRA_DIST = \ asciidoc.conf \ asciidoc-override.css \ + alpm-hooks.5.txt \ pacman.8.txt \ makepkg.8.txt \ makepkg-template.1.txt \ @@ -147,6 +150,7 @@ $(HTML_OTHER): asciidoc.conf Makefile.am %.3.html: ASCIIDOC_OPTS += -d manpage # Dependency rules +alpm-hooks.5 alpm-hooks.5.html: alpm-hooks.5.txt pacman.8 pacman.8.html: pacman.8.txt makepkg.8 makepkg.8.html: makepkg.8.txt makepkg-template.1 makepkg-template.1.html: makepkg-template.1.txt diff --git a/doc/alpm-hooks.5.txt b/doc/alpm-hooks.5.txt new file mode 100644 index 0000000..2986abf --- /dev/null +++ b/doc/alpm-hooks.5.txt @@ -0,0 +1,118 @@ +///// +vim:set ts=4 sw=4 syntax=asciidoc noet spell spelllang=en_us: +///// +alpm-hooks(5) +============= + +NAME +---- + +alpm-hooks - alpm hook file format + +SYNOPSIS +-------- + +-------- +[Trigger] (Required, Repeatable) +Operation = Install|Upgrade|Remove (Required, Repeatable) +Type = File|Package (Required) +Target = <Path|PkgName> (Required, Repeatable) + +[Action] (Required) +When = PreTransaction|PostTransaction (Required) +Exec = <Command> (Required) +Depends = <PkgName> (Optional) +AbortOnFail (Optional, PreTransaction only) +-------- + +DESCRIPTION +----------- + +libalpm provides the ability to specify hooks to run before or after +transactions based on the packages and/or files being modified. Hooks consist +of a single '[Action]' section describing the action to be run and one or more +'[Trigger]' section describing which transactions it should be run for. + +TRIGGERS +-------- + +Hooks must contain at least one '[Trigger]' section that determines which +transactions will cause the hook to run. If multiple trigger sections are +defined the hook will run if the transaction matches *any* of the triggers. + +*Operation =* Install|Upgrade|Remove:: + Select the type of operation to match targets against. May be specified + multiple times. Installations are considered an upgrade if the package or + file is already present on the system regardless of whether the new package + version is actually greater than the currently installed version. For File + triggers, this is true even if the file changes ownership from one package + to another. Required. + +*Type =* File|Package:: + Select whether targets are matched against transaction packages or files. + See CAVEATS for special notes regarding File triggers. Required. + +*Target =* <path|package>:: + The file path or package name to match against the active transaction. + File paths refer to the files in the package archive; the installation root + should *not* be included in the path. Shell-style glob patterns are + allowed. It is possible to invert matches by prepending a file with an + exclamation mark. May be specified multiple times. Required. + +ACTIONS +------- + +*Exec =* /path/to/executable:: + Executable to run. Required. + +*When =* PreTransaction|PostTransaction:: + When to run the hook. Required. + +*Depends =* <package>:: + Packages that must be installed for the hook to run. May be specified + multiple times. + +*AbortOnFail*:: + Causes the transaction to be aborted if the hook exits non-zero. Only + applies to PreTransaction hooks. + +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'. + +EXAMPLES +-------- + +-------- +# Force disks to sync to reduce the risk of data corruption + +[Trigger] +Operation = Install +Operation = Upgrade +Operation = Remove +Type = Package +Target = * + +[Action] +Depends = coreutils +When = PostTransaction +Exec = /usr/bin/sync +-------- + +CAVEATS +------- + +There are situations when file triggers may act in unexpected ways. Hooks are +triggered using the file list of the installed, upgraded, or removed package. +When installing or upgrading a file that is extracted with a '.pacnew' +extension, the original file name is used in triggering the hook. When +removing a package, all files owned by that package can trigger a hook whether +or not they were actually present on the file system before package removal. + +PostTransaction hooks will *not* run if the transaction fails to complete for +any reason. + +include::footer.txt[] -- 2.6.2
On 27/10/15 14:47, Andrew Gregory wrote:
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> ---
Updated caveats section.
doc/.gitignore | 1 + doc/Makefile.am | 4 ++ doc/alpm-hooks.5.txt | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 doc/alpm-hooks.5.txt
Looks good. Allan
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- Replaced wordexp with a custom function that only does word splitting with quote removal. doc/alpm-hooks.5.txt | 5 +- lib/libalpm/hook.c | 128 +++++++++++++++++++++++++- test/pacman/tests/TESTS | 1 + test/pacman/tests/hook-exec-with-arguments.py | 22 +++++ 4 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 test/pacman/tests/hook-exec-with-arguments.py diff --git a/doc/alpm-hooks.5.txt b/doc/alpm-hooks.5.txt index 2986abf..770f466 100644 --- a/doc/alpm-hooks.5.txt +++ b/doc/alpm-hooks.5.txt @@ -62,8 +62,9 @@ defined the hook will run if the transaction matches *any* of the triggers. ACTIONS ------- -*Exec =* /path/to/executable:: - Executable to run. Required. +*Exec =* <command>:: + Command to run. Command arguments are split on whitespace. Values + containing whitespace should be enclosed in quotes. Required. *When =* PreTransaction|PostTransaction:: When to run the hook. Required. diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index 463b76a..45c10e1 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -17,6 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <ctype.h> #include <dirent.h> #include <errno.h> #include <string.h> @@ -49,7 +50,7 @@ struct _alpm_hook_t { char *name; alpm_list_t *triggers; alpm_list_t *depends; - char *cmd; + char **cmd; enum _alpm_hook_when_t when; int abort_on_fail; }; @@ -67,11 +68,22 @@ static void _alpm_trigger_free(struct _alpm_trigger_t *trigger) } } +static void _alpm_wordsplit_free(char **ws) +{ + if(ws) { + char **c; + for(c = ws; *c; c++) { + free(*c); + } + free(ws); + } +} + static void _alpm_hook_free(struct _alpm_hook_t *hook) { if(hook) { free(hook->name); - free(hook->cmd); + _alpm_wordsplit_free(hook->cmd); alpm_list_free_inner(hook->triggers, (alpm_list_fn_free) _alpm_trigger_free); alpm_list_free(hook->triggers); FREELIST(hook->depends); @@ -141,6 +153,106 @@ static int _alpm_hook_validate(alpm_handle_t *handle, return ret; } +static char **_alpm_wordsplit(char *str) +{ + char *c = str, *end; + char **out = NULL, **outsave; + size_t count = 0; + + if(str == NULL) { + errno = EINVAL; + return NULL; + } + + for(c = str; isspace(*c); c++); + while(*c) { + size_t wordlen = 0; + + /* extend our array */ + outsave = out; + if((out = realloc(out, (count + 1) * sizeof(char*))) == NULL) { + out = outsave; + goto error; + } + + /* calculate word length and check for unbalanced quotes */ + for(end = c; *end && !isspace(*end); end++) { + if(*end == '\'' || *end == '"') { + char quote = *end; + while(*(++end) && *end != quote) { + if(*end == '\\' && *(end + 1) == quote) { + end++; + } + wordlen++; + } + if(*end != quote) { + errno = EINVAL; + goto error; + } + } else { + if(*end == '\\' && (end[1] == '\'' || end[1] == '"')) { + end++; /* skip the '\\' */ + } + wordlen++; + } + } + + if(wordlen == (size_t) (end - c)) { + /* no internal quotes or escapes, copy it the easy way */ + if((out[count++] = strndup(c, wordlen)) == NULL) { + goto error; + } + } else { + /* manually copy to remove quotes and escapes */ + char *dest = out[count++] = malloc(wordlen + 1); + if(dest == NULL) { goto error; } + while(c < end) { + if(*c == '\'' || *c == '"') { + char quote = *c; + /* we know there must be a matching end quote, + * no need to check for '\0' */ + for(c++; *c != quote; c++) { + if(*c == '\\' && *(c + 1) == quote) { + c++; + } + *(dest++) = *c; + } + c++; + } else { + if(*c == '\\' && (c[1] == '\'' || c[1] == '"')) { + c++; /* skip the '\\' */ + } + *(dest++) = *(c++); + } + } + *dest = '\0'; + } + + if(*end == '\0') { + break; + } else { + for(c = end + 1; isspace(*c); c++); + } + } + + outsave = out; + if((out = realloc(out, (count + 1) * sizeof(char*))) == NULL) { + out = outsave; + goto error; + } + + out[count++] = NULL; + + return out; + +error: + /* can't use wordsplit_free here because NULL has not been appended */ + while(count) { + free(out[--count]); + } + free(out); + return NULL; +} static int _alpm_hook_parse_cb(const char *file, int line, const char *section, char *key, char *value, void *data) { @@ -208,7 +320,14 @@ static int _alpm_hook_parse_cb(const char *file, int line, } else if(strcmp(key, "AbortOnFail") == 0) { hook->abort_on_fail = 1; } else if(strcmp(key, "Exec") == 0) { - STRDUP(hook->cmd, value, return 1); + if((hook->cmd = _alpm_wordsplit(value)) == NULL) { + if(errno == EINVAL) { + error(_("hook %s line %d: invalid value %s\n"), file, line, value); + } else { + error(_("hook %s line %d: unable to set option (%s)\n"), + file, line, strerror(errno)); + } + } } else { error(_("hook %s line %d: invalid option %s\n"), file, line, key); } @@ -383,7 +502,6 @@ static alpm_list_t *find_hook(alpm_list_t *haystack, const void *needle) static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) { alpm_list_t *i, *pkgs = _alpm_db_get_pkgcache(handle->db_local); - char *const argv[] = { hook->cmd, NULL }; for(i = hook->depends; i; i = i->next) { if(!alpm_find_satisfier(pkgs, i->data)) { @@ -393,7 +511,7 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) } } - return _alpm_run_chroot(handle, hook->cmd, argv); + return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd); } int _alpm_hook_run(alpm_handle_t *handle, enum _alpm_hook_when_t when) diff --git a/test/pacman/tests/TESTS b/test/pacman/tests/TESTS index 8ad1b9c..afd2e69 100644 --- a/test/pacman/tests/TESTS +++ b/test/pacman/tests/TESTS @@ -51,6 +51,7 @@ TESTS += test/pacman/tests/fileconflict030.py TESTS += test/pacman/tests/fileconflict031.py TESTS += test/pacman/tests/fileconflict032.py TESTS += test/pacman/tests/hook-abortonfail.py +TESTS += test/pacman/tests/hook-exec-with-arguments.py TESTS += test/pacman/tests/hook-file-change-packages.py TESTS += test/pacman/tests/hook-file-remove-trigger-match.py TESTS += test/pacman/tests/hook-file-upgrade-nomatch.py diff --git a/test/pacman/tests/hook-exec-with-arguments.py b/test/pacman/tests/hook-exec-with-arguments.py new file mode 100644 index 0000000..d3df87b --- /dev/null +++ b/test/pacman/tests/hook-exec-with-arguments.py @@ -0,0 +1,22 @@ +self.description = "Hook with arguments" + +self.add_hook("hook", + """ + [Trigger] + Type = Package + Operation = Install + Target = foo + + [Action] + When = PreTransaction + Exec = bin/sh -c ': > hook-output' + """); + +sp = pmpkg("foo") +self.addpkg2db("sync", sp) + +self.args = "-S foo" + +self.addrule("PACMAN_RETCODE=0") +self.addrule("PKG_EXIST=foo") +self.addrule("FILE_EXIST=hook-output") -- 2.6.2
On 27/10/15 14:47, Andrew Gregory wrote:
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> ---
Replaced wordexp with a custom function that only does word splitting with quote removal.
Looks fine to me, apart from the indentation issues (space vs tab). A
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- lib/libalpm/hook.c | 2 +- lib/libalpm/trans.c | 2 +- lib/libalpm/util.c | 174 ++++++++++++++++++++++++++++++++++++++++++++-------- lib/libalpm/util.h | 5 +- 4 files changed, 154 insertions(+), 29 deletions(-) diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index 45c10e1..b449ce0 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -511,7 +511,7 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) } } - return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd); + return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, NULL, NULL); } int _alpm_hook_run(alpm_handle_t *handle, enum _alpm_hook_when_t when) diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index a6b1aef..06997a0 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -391,7 +391,7 @@ int _alpm_runscriptlet(alpm_handle_t *handle, const char *filepath, _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\"\n", cmdline); - retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv); + retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv, NULL, NULL); cleanup: if(scriptfn && unlink(scriptfn)) { diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 66a2742..eed0293 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -31,6 +31,7 @@ #include <limits.h> #include <sys/wait.h> #include <fnmatch.h> +#include <poll.h> /* libarchive */ #include <archive.h> @@ -445,16 +446,40 @@ ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, return files; } +/* write wrapper that ignores SIGPIPE */ +static ssize_t _alpm_pipe_write(int fd, const void *buf, size_t count) +{ + sigset_t new, old; + ssize_t ret; + sigemptyset(&new); + sigaddset(&new, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &new, &old); + ret = write(fd, buf, count); + pthread_sigmask(SIG_SETMASK, &old, NULL); + return ret; +} + +static void _alpm_handle_script_output(alpm_handle_t *handle, const char *line) +{ + alpm_event_scriptlet_info_t event = { + .type = ALPM_EVENT_SCRIPTLET_INFO, + .line = line + }; + alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line); + EVENT(handle, &event); +} + /** Execute a command with arguments in a chroot. * @param handle the context handle * @param cmd command to execute * @param argv arguments to pass to cmd * @return 0 on success, 1 on error */ -int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) +int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[], + _alpm_cb_io p2c_cb, void *p2c_ctx) { pid_t pid; - int pipefd[2], cwdfd; + int c2p_pipefd[2], p2c_pipefd[2], cwdfd; int retval = 0; /* save the cwd so we can restore it later */ @@ -476,7 +501,13 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) /* Flush open fds before fork() to avoid cloning buffers */ fflush(NULL); - if(pipe(pipefd) == -1) { + if(pipe(c2p_pipefd) == -1) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); + retval = 1; + goto cleanup; + } + + if(p2c_cb && pipe(p2c_pipefd) == -1) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); retval = 1; goto cleanup; @@ -495,10 +526,15 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) close(0); close(1); close(2); - while(dup2(pipefd[1], 1) == -1 && errno == EINTR); - while(dup2(pipefd[1], 2) == -1 && errno == EINTR); - close(pipefd[0]); - close(pipefd[1]); + while(dup2(c2p_pipefd[1], 1) == -1 && errno == EINTR); + while(dup2(c2p_pipefd[1], 2) == -1 && errno == EINTR); + if(p2c_cb) { + while(dup2(p2c_pipefd[0], 0) == -1 && errno == EINTR); + close(p2c_pipefd[0]); + close(p2c_pipefd[1]); + } + close(c2p_pipefd[0]); + close(c2p_pipefd[1]); if(cwdfd >= 0) { close(cwdfd); } @@ -521,29 +557,115 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) } else { /* this code runs for the parent only (wait on the child) */ int status; - FILE *pipe_file; - - close(pipefd[1]); - pipe_file = fdopen(pipefd[0], "r"); - if(pipe_file == NULL) { - close(pipefd[0]); - retval = 1; + char obuf[PIPE_BUF]; /* writes <= PIPE_BUF are guaranteed atomic */ + char ibuf[LINE_MAX + 2]; /* +2 for appending \n\0 */ + ssize_t olen = 0, ilen = 0; + struct pollfd fds[2], *c2p = &(fds[0]), *p2c = &(fds[1]); + nfds_t nfds = 2; + + c2p->fd = c2p_pipefd[0]; + c2p->events = POLLIN; + fcntl(c2p->fd, F_SETFL, O_NONBLOCK); + close(c2p_pipefd[1]); + + if(p2c_cb) { + p2c->fd = p2c_pipefd[1]; + p2c->events = POLLOUT; + fcntl(p2c->fd, F_SETFL, O_NONBLOCK); + close(p2c_pipefd[0]); } else { - while(!feof(pipe_file)) { - char line[PATH_MAX]; - alpm_event_scriptlet_info_t event = { - .type = ALPM_EVENT_SCRIPTLET_INFO, - .line = line - }; - if(safe_fgets(line, PATH_MAX, pipe_file) == NULL) { - break; + p2c->fd = -1; + p2c->events = 0; + } + +#define STOP_POLLING(p) do { close(p->fd); p->fd = -1; } while(0) + + while((c2p->fd != -1 || p2c->fd != -1) && poll(fds, nfds, -1) > 0) { + if(c2p->revents & POLLIN) { + ssize_t space = LINE_MAX - ilen; + ssize_t nread = read(c2p->fd, ibuf + ilen, space); + if(nread > 0) { + char *newline = memchr(ibuf + ilen, '\n', nread); + ilen += nread; + if(newline) { + while(newline) { + size_t linelen = newline - ibuf + 1; + char old = ibuf[linelen]; + ibuf[linelen] = '\0'; + _alpm_handle_script_output(handle, ibuf); + ibuf[linelen] = old; + + ilen -= linelen; + memmove(ibuf, ibuf + linelen, ilen); + newline = memchr(ibuf, '\n', ilen); + } + } else if(nread == space) { + /* we didn't read a full line, but we're out of space */ + strcpy(ibuf + ilen, "\n"); + _alpm_handle_script_output(handle, ibuf); + ilen = 0; + } + } else if(nread == 0) { + /* end-of-file */ + if(ilen) { + strcpy(ibuf + ilen, "\n"); + _alpm_handle_script_output(handle, ibuf); + } + STOP_POLLING(c2p); + } else if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + /* nothing read, try again */ + } else { + /* read error */ + if(ilen) { + strcpy(ibuf + ilen, "\n"); + _alpm_handle_script_output(handle, ibuf); + } + _alpm_log(handle, ALPM_LOG_ERROR, + _("unable to read from pipe (%s)\n"), strerror(errno)); + STOP_POLLING(c2p); + } + } else if(c2p->revents) { + /* anything but POLLIN indicates an error */ + STOP_POLLING(c2p); + } + if(p2c->revents & POLLOUT) { + ssize_t nwrite; + if(olen == 0) { + /* empty buffer, ask the callback for more */ + if((olen = p2c_cb(obuf, PIPE_BUF, p2c_ctx)) == 0) { + /* no more to write, close the pipe */ + STOP_POLLING(p2c); + continue; + } + } + if((nwrite = _alpm_pipe_write(p2c->fd, obuf, olen)) != -1) { + olen -= nwrite; + memmove(obuf, obuf + nwrite, olen); + } else { + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + /* nothing written, try again later */ + } else { + /* something went wrong, close the pipe */ + _alpm_log(handle, ALPM_LOG_ERROR, + _("unable to write to pipe (%s)\n"), strerror(errno)); + STOP_POLLING(p2c); + } } - alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line); - EVENT(handle, &event); + } else if(p2c->revents) { + /* anything but POLLOUT indicates an error */ + STOP_POLLING(p2c); } - fclose(pipe_file); } + if(p2c->fd != -1) { + close(p2c->fd); + } + if(c2p->fd != -1) { + close(c2p->fd); + } + +#undef STOP_POLLING + while(waitpid(pid, &status, 0) == -1) { if(errno != EINTR) { _alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno)); @@ -605,7 +727,7 @@ int _alpm_ldconfig(alpm_handle_t *handle) char arg0[32]; char *argv[] = { arg0, NULL }; strcpy(arg0, "ldconfig"); - return _alpm_run_chroot(handle, LDCONFIG, argv); + return _alpm_run_chroot(handle, LDCONFIG, argv, NULL, NULL); } } diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h index 95112cf..c0c9ff0 100644 --- a/lib/libalpm/util.h +++ b/lib/libalpm/util.h @@ -119,7 +119,10 @@ int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix, ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, int full_count); -int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]); +typedef ssize_t (*_alpm_cb_io)(void *buf, ssize_t len, void *ctx); + +int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[], + _alpm_cb_io in_cb, void *in_ctx); int _alpm_ldconfig(alpm_handle_t *handle); int _alpm_str_cmp(const void *s1, const void *s2); char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename); -- 2.6.2
On 27/10/15 14:47, Andrew Gregory wrote:
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- lib/libalpm/hook.c | 2 +- lib/libalpm/trans.c | 2 +- lib/libalpm/util.c | 174 ++++++++++++++++++++++++++++++++++++++++++++-------- lib/libalpm/util.h | 5 +- 4 files changed, 154 insertions(+), 29 deletions(-)
diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index 45c10e1..b449ce0 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -511,7 +511,7 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) } }
- return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd); + return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, NULL, NULL); }
int _alpm_hook_run(alpm_handle_t *handle, enum _alpm_hook_when_t when)
OK.
diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index a6b1aef..06997a0 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -391,7 +391,7 @@ int _alpm_runscriptlet(alpm_handle_t *handle, const char *filepath,
_alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\"\n", cmdline);
- retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv); + retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv, NULL, NULL);
cleanup: if(scriptfn && unlink(scriptfn)) {
OK.
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 66a2742..eed0293 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -31,6 +31,7 @@ #include <limits.h> #include <sys/wait.h> #include <fnmatch.h> +#include <poll.h>
/* libarchive */ #include <archive.h> @@ -445,16 +446,40 @@ ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, return files; }
+/* write wrapper that ignores SIGPIPE */
write() wrapper ... Also, this comment is worth expanding - why do we need this?
+static ssize_t _alpm_pipe_write(int fd, const void *buf, size_t count) +{ + sigset_t new, old; + ssize_t ret; + sigemptyset(&new); + sigaddset(&new, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &new, &old); + ret = write(fd, buf, count); + pthread_sigmask(SIG_SETMASK, &old, NULL); + return ret; +} +
Why not use signal(SIGPIPE, SIG_IGN)?
+static void _alpm_handle_script_output(alpm_handle_t *handle, const char *line) +{ + alpm_event_scriptlet_info_t event = { + .type = ALPM_EVENT_SCRIPTLET_INFO, + .line = line + }; + alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line); + EVENT(handle, &event); +} + /** Execute a command with arguments in a chroot. * @param handle the context handle * @param cmd command to execute * @param argv arguments to pass to cmd
Needs updated.
* @return 0 on success, 1 on error */ -int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) +int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[], + _alpm_cb_io p2c_cb, void *p2c_ctx)
void *... can this be alpm_list_t?
{ pid_t pid; - int pipefd[2], cwdfd; + int c2p_pipefd[2], p2c_pipefd[2], cwdfd;
child2parent, parent2child - I would like these names expanded.
int retval = 0;
/* save the cwd so we can restore it later */ @@ -476,7 +501,13 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) /* Flush open fds before fork() to avoid cloning buffers */ fflush(NULL);
- if(pipe(pipefd) == -1) { + if(pipe(c2p_pipefd) == -1) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); + retval = 1; + goto cleanup; + } + + if(p2c_cb && pipe(p2c_pipefd) == -1) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); retval = 1; goto cleanup;
OK
@@ -495,10 +526,15 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) close(0); close(1); close(2); - while(dup2(pipefd[1], 1) == -1 && errno == EINTR); - while(dup2(pipefd[1], 2) == -1 && errno == EINTR); - close(pipefd[0]); - close(pipefd[1]); + while(dup2(c2p_pipefd[1], 1) == -1 && errno == EINTR); + while(dup2(c2p_pipefd[1], 2) == -1 && errno == EINTR); + if(p2c_cb) { + while(dup2(p2c_pipefd[0], 0) == -1 && errno == EINTR); + close(p2c_pipefd[0]); + close(p2c_pipefd[1]); + } + close(c2p_pipefd[0]); + close(c2p_pipefd[1]); if(cwdfd >= 0) { close(cwdfd); }
OK
@@ -521,29 +557,115 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) } else { /* this code runs for the parent only (wait on the child) */ int status; - FILE *pipe_file; - - close(pipefd[1]); - pipe_file = fdopen(pipefd[0], "r"); - if(pipe_file == NULL) { - close(pipefd[0]); - retval = 1; + char obuf[PIPE_BUF]; /* writes <= PIPE_BUF are guaranteed atomic */ + char ibuf[LINE_MAX + 2]; /* +2 for appending \n\0 */ + ssize_t olen = 0, ilen = 0; + struct pollfd fds[2], *c2p = &(fds[0]), *p2c = &(fds[1]); + nfds_t nfds = 2; + + c2p->fd = c2p_pipefd[0]; + c2p->events = POLLIN; + fcntl(c2p->fd, F_SETFL, O_NONBLOCK); + close(c2p_pipefd[1]); +
OK
+ if(p2c_cb) { + p2c->fd = p2c_pipefd[1]; + p2c->events = POLLOUT; + fcntl(p2c->fd, F_SETFL, O_NONBLOCK); + close(p2c_pipefd[0]); } else { - while(!feof(pipe_file)) { - char line[PATH_MAX]; - alpm_event_scriptlet_info_t event = { - .type = ALPM_EVENT_SCRIPTLET_INFO, - .line = line - }; - if(safe_fgets(line, PATH_MAX, pipe_file) == NULL) { - break; + p2c->fd = -1; + p2c->events = 0; + }
OK And, I am lost from here...
+ +#define STOP_POLLING(p) do { close(p->fd); p->fd = -1; } while(0) + + while((c2p->fd != -1 || p2c->fd != -1) && poll(fds, nfds, -1) > 0) { + if(c2p->revents & POLLIN) { + ssize_t space = LINE_MAX - ilen; + ssize_t nread = read(c2p->fd, ibuf + ilen, space); + if(nread > 0) { + char *newline = memchr(ibuf + ilen, '\n', nread); + ilen += nread; + if(newline) { + while(newline) { + size_t linelen = newline - ibuf + 1; + char old = ibuf[linelen]; + ibuf[linelen] = '\0'; + _alpm_handle_script_output(handle, ibuf); + ibuf[linelen] = old; + + ilen -= linelen; + memmove(ibuf, ibuf + linelen, ilen); + newline = memchr(ibuf, '\n', ilen); + } + } else if(nread == space) { + /* we didn't read a full line, but we're out of space */ + strcpy(ibuf + ilen, "\n"); + _alpm_handle_script_output(handle, ibuf); + ilen = 0; + } + } else if(nread == 0) { + /* end-of-file */ + if(ilen) { + strcpy(ibuf + ilen, "\n"); + _alpm_handle_script_output(handle, ibuf); + } + STOP_POLLING(c2p); + } else if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + /* nothing read, try again */ + } else { + /* read error */ + if(ilen) { + strcpy(ibuf + ilen, "\n"); + _alpm_handle_script_output(handle, ibuf); + } + _alpm_log(handle, ALPM_LOG_ERROR, + _("unable to read from pipe (%s)\n"), strerror(errno)); + STOP_POLLING(c2p); + } + } else if(c2p->revents) { + /* anything but POLLIN indicates an error */ + STOP_POLLING(c2p); + } + if(p2c->revents & POLLOUT) { + ssize_t nwrite; + if(olen == 0) { + /* empty buffer, ask the callback for more */ + if((olen = p2c_cb(obuf, PIPE_BUF, p2c_ctx)) == 0) { + /* no more to write, close the pipe */ + STOP_POLLING(p2c); + continue; + } + } + if((nwrite = _alpm_pipe_write(p2c->fd, obuf, olen)) != -1) { + olen -= nwrite; + memmove(obuf, obuf + nwrite, olen); + } else { + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + /* nothing written, try again later */ + } else { + /* something went wrong, close the pipe */ + _alpm_log(handle, ALPM_LOG_ERROR, + _("unable to write to pipe (%s)\n"), strerror(errno)); + STOP_POLLING(p2c); + } } - alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line); - EVENT(handle, &event); + } else if(p2c->revents) { + /* anything but POLLOUT indicates an error */ + STOP_POLLING(p2c); } - fclose(pipe_file); }
... to here. This has gone from being a large function to a massive one - is there any way to split it up into smaller components?
+ if(p2c->fd != -1) { + close(p2c->fd); + } + if(c2p->fd != -1) { + close(c2p->fd); + } + +#undef STOP_POLLING +
OK
while(waitpid(pid, &status, 0) == -1) { if(errno != EINTR) { _alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno)); @@ -605,7 +727,7 @@ int _alpm_ldconfig(alpm_handle_t *handle) char arg0[32]; char *argv[] = { arg0, NULL }; strcpy(arg0, "ldconfig"); - return _alpm_run_chroot(handle, LDCONFIG, argv); + return _alpm_run_chroot(handle, LDCONFIG, argv, NULL, NULL); } }
OK
diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h index 95112cf..c0c9ff0 100644 --- a/lib/libalpm/util.h +++ b/lib/libalpm/util.h @@ -119,7 +119,10 @@ int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix,
ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, int full_count);
-int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]); +typedef ssize_t (*_alpm_cb_io)(void *buf, ssize_t len, void *ctx); + +int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[], + _alpm_cb_io in_cb, void *in_ctx); int _alpm_ldconfig(alpm_handle_t *handle); int _alpm_str_cmp(const void *s1, const void *s2); char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename);
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- doc/alpm-hooks.5.txt | 5 ++ lib/libalpm/hook.c | 126 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 114 insertions(+), 17 deletions(-) diff --git a/doc/alpm-hooks.5.txt b/doc/alpm-hooks.5.txt index 770f466..3729387 100644 --- a/doc/alpm-hooks.5.txt +++ b/doc/alpm-hooks.5.txt @@ -23,6 +23,7 @@ When = PreTransaction|PostTransaction (Required) Exec = <Command> (Required) Depends = <PkgName> (Optional) AbortOnFail (Optional, PreTransaction only) +NeedsTargets (Optional) -------- DESCRIPTION @@ -77,6 +78,10 @@ ACTIONS Causes the transaction to be aborted if the hook exits non-zero. Only applies to PreTransaction hooks. +*NeedsTargets*:: + Causes the list of matched trigger targets to be passed to the running hook + on 'stdin'. + OVERRIDING HOOKS ---------------- diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index b449ce0..3a2c33a 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -51,8 +51,9 @@ struct _alpm_hook_t { alpm_list_t *triggers; alpm_list_t *depends; char **cmd; + alpm_list_t *matches; enum _alpm_hook_when_t when; - int abort_on_fail; + int abort_on_fail, needs_targets; }; struct _alpm_hook_cb_ctx { @@ -86,6 +87,7 @@ static void _alpm_hook_free(struct _alpm_hook_t *hook) _alpm_wordsplit_free(hook->cmd); alpm_list_free_inner(hook->triggers, (alpm_list_fn_free) _alpm_trigger_free); alpm_list_free(hook->triggers); + alpm_list_free(hook->matches); FREELIST(hook->depends); free(hook); } @@ -319,6 +321,8 @@ static int _alpm_hook_parse_cb(const char *file, int line, hook->depends = alpm_list_add(hook->depends, val); } else if(strcmp(key, "AbortOnFail") == 0) { hook->abort_on_fail = 1; + } else if(strcmp(key, "NeedsTargets") == 0) { + hook->needs_targets = 1; } else if(strcmp(key, "Exec") == 0) { if((hook->cmd = _alpm_wordsplit(value)) == NULL) { if(errno == EINVAL) { @@ -338,7 +342,8 @@ static int _alpm_hook_parse_cb(const char *file, int line, return 0; } -static int _alpm_hook_trigger_match_file(alpm_handle_t *handle, struct _alpm_trigger_t *t) +static int _alpm_hook_trigger_match_file(alpm_handle_t *handle, + struct _alpm_hook_t *hook, struct _alpm_trigger_t *t) { alpm_list_t *i, *j, *install = NULL, *upgrade = NULL, *remove = NULL; size_t isize = 0, rsize = 0; @@ -422,15 +427,31 @@ static int _alpm_hook_trigger_match_file(alpm_handle_t *handle, struct _alpm_tri || (t->op & ALPM_HOOK_OP_UPGRADE && upgrade) || (t->op & ALPM_HOOK_OP_REMOVE && remove); - alpm_list_free(install); - alpm_list_free(upgrade); - alpm_list_free(remove); + if(hook->needs_targets) { +#define _save_matches(_op, _matches) \ + if(t->op & _op && _matches) { \ + hook->matches = alpm_list_join(hook->matches, _matches); \ + } else { \ + alpm_list_free(_matches); \ + } + _save_matches(ALPM_HOOK_OP_INSTALL, install); + _save_matches(ALPM_HOOK_OP_UPGRADE, upgrade); + _save_matches(ALPM_HOOK_OP_REMOVE, remove); +#undef _save_matches + } else { + alpm_list_free(install); + alpm_list_free(upgrade); + alpm_list_free(remove); + } return ret; } -static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, struct _alpm_trigger_t *t) +static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, + struct _alpm_hook_t *hook, struct _alpm_trigger_t *t) { + alpm_list_t *install = NULL, *upgrade = NULL, *remove = NULL; + if(t->op & ALPM_HOOK_OP_INSTALL || t->op & ALPM_HOOK_OP_UPGRADE) { alpm_list_t *i; for(i = handle->trans->add; i; i = i->next) { @@ -438,11 +459,19 @@ static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, struct _alpm_trig if(_alpm_fnmatch_patterns(t->targets, pkg->name) == 0) { if(alpm_db_get_pkg(handle->db_local, pkg->name)) { if(t->op & ALPM_HOOK_OP_UPGRADE) { - return 1; + if(hook->needs_targets) { + upgrade = alpm_list_add(upgrade, pkg->name); + } else { + return 1; + } } } else { if(t->op & ALPM_HOOK_OP_INSTALL) { - return 1; + if(hook->needs_targets) { + install = alpm_list_add(install, pkg->name); + } else { + return 1; + } } } } @@ -455,31 +484,47 @@ static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, struct _alpm_trig alpm_pkg_t *pkg = i->data; if(pkg && _alpm_fnmatch_patterns(t->targets, pkg->name) == 0) { if(!alpm_list_find(handle->trans->add, pkg, _alpm_pkg_cmp)) { - return 1; + if(hook->needs_targets) { + remove = alpm_list_add(remove, pkg->name); + } else { + return 1; + } } } } } - return 0; + /* if we reached this point we either need the target lists or we didn't + * match anything and the following calls will all be no-ops */ + hook->matches = alpm_list_join(hook->matches, install); + hook->matches = alpm_list_join(hook->matches, upgrade); + hook->matches = alpm_list_join(hook->matches, remove); + + return install || upgrade || remove; } -static int _alpm_hook_trigger_match(alpm_handle_t *handle, struct _alpm_trigger_t *t) +static int _alpm_hook_trigger_match(alpm_handle_t *handle, + struct _alpm_hook_t *hook, struct _alpm_trigger_t *t) { return t->type == ALPM_HOOK_TYPE_PACKAGE - ? _alpm_hook_trigger_match_pkg(handle, t) - : _alpm_hook_trigger_match_file(handle, t); + ? _alpm_hook_trigger_match_pkg(handle, hook, t) + : _alpm_hook_trigger_match_file(handle, hook, t); } static int _alpm_hook_triggered(alpm_handle_t *handle, struct _alpm_hook_t *hook) { alpm_list_t *i; + int ret = 0; for(i = hook->triggers; i; i = i->next) { - if(_alpm_hook_trigger_match(handle, i->data)) { - return 1; + if(_alpm_hook_trigger_match(handle, hook, i->data)) { + if(!hook->needs_targets) { + return 1; + } else { + ret = 1; + } } } - return 0; + return ret; } static int _alpm_hook_cmp(struct _alpm_hook_t *h1, struct _alpm_hook_t *h2) @@ -499,6 +544,44 @@ static alpm_list_t *find_hook(alpm_list_t *haystack, const void *needle) return NULL; } +static ssize_t _alpm_hook_feed_targets(char *buf, ssize_t needed, alpm_list_t **pos) +{ + size_t remaining = needed, written = 0;; + size_t len; + + while(*pos && (len = strlen((*pos)->data)) + 1 <= remaining) { + memcpy(buf, (*pos)->data, len); + buf[len++] = '\n'; + *pos = (*pos)->next; + buf += len; + remaining -= len; + written += len; + } + + if(*pos && remaining) { + memcpy(buf, (*pos)->data, remaining); + (*pos)->data = (char*) (*pos)->data + remaining; + written += remaining; + } + + return written; +} + +static alpm_list_t *_alpm_strlist_dedup(alpm_list_t *list) +{ + alpm_list_t *i = list; + while(i) { + alpm_list_t *next = i->next; + while(next && strcmp(i->data, next->data) == 0) { + list = alpm_list_remove_item(list, next); + free(next); + next = i->next; + } + i = next; + } + return list; +} + static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) { alpm_list_t *i, *pkgs = _alpm_db_get_pkgcache(handle->db_local); @@ -511,7 +594,16 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) } } - return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, NULL, NULL); + if(hook->needs_targets) { + alpm_list_t *ctx; + hook->matches = alpm_list_msort(hook->matches, + alpm_list_count(hook->matches), (alpm_list_fn_cmp)strcmp); + ctx = hook->matches = _alpm_strlist_dedup(hook->matches); + return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, + (_alpm_cb_io) _alpm_hook_feed_targets, &ctx); + } else { + return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, NULL, NULL); + } } int _alpm_hook_run(alpm_handle_t *handle, enum _alpm_hook_when_t when) -- 2.6.2
On 27/10/15 14:47, Andrew Gregory wrote:
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- doc/alpm-hooks.5.txt | 5 ++ lib/libalpm/hook.c | 126 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 114 insertions(+), 17 deletions(-)
diff --git a/doc/alpm-hooks.5.txt b/doc/alpm-hooks.5.txt index 770f466..3729387 100644 --- a/doc/alpm-hooks.5.txt +++ b/doc/alpm-hooks.5.txt @@ -23,6 +23,7 @@ When = PreTransaction|PostTransaction (Required) Exec = <Command> (Required) Depends = <PkgName> (Optional) AbortOnFail (Optional, PreTransaction only) +NeedsTargets (Optional) --------
DESCRIPTION @@ -77,6 +78,10 @@ ACTIONS Causes the transaction to be aborted if the hook exits non-zero. Only applies to PreTransaction hooks.
+*NeedsTargets*:: + Causes the list of matched trigger targets to be passed to the running hook + on 'stdin'. + OVERRIDING HOOKS ----------------
OK.
diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index b449ce0..3a2c33a 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -51,8 +51,9 @@ struct _alpm_hook_t { alpm_list_t *triggers; alpm_list_t *depends; char **cmd; + alpm_list_t *matches; enum _alpm_hook_when_t when; - int abort_on_fail; + int abort_on_fail, needs_targets; };
OK
struct _alpm_hook_cb_ctx { @@ -86,6 +87,7 @@ static void _alpm_hook_free(struct _alpm_hook_t *hook) _alpm_wordsplit_free(hook->cmd); alpm_list_free_inner(hook->triggers, (alpm_list_fn_free) _alpm_trigger_free); alpm_list_free(hook->triggers); + alpm_list_free(hook->matches); FREELIST(hook->depends); free(hook); }
OK
@@ -319,6 +321,8 @@ static int _alpm_hook_parse_cb(const char *file, int line, hook->depends = alpm_list_add(hook->depends, val); } else if(strcmp(key, "AbortOnFail") == 0) { hook->abort_on_fail = 1; + } else if(strcmp(key, "NeedsTargets") == 0) { + hook->needs_targets = 1; } else if(strcmp(key, "Exec") == 0) { if((hook->cmd = _alpm_wordsplit(value)) == NULL) { if(errno == EINVAL) {
OK
@@ -338,7 +342,8 @@ static int _alpm_hook_parse_cb(const char *file, int line, return 0; }
-static int _alpm_hook_trigger_match_file(alpm_handle_t *handle, struct _alpm_trigger_t *t) +static int _alpm_hook_trigger_match_file(alpm_handle_t *handle, + struct _alpm_hook_t *hook, struct _alpm_trigger_t *t) { alpm_list_t *i, *j, *install = NULL, *upgrade = NULL, *remove = NULL; size_t isize = 0, rsize = 0; @@ -422,15 +427,31 @@ static int _alpm_hook_trigger_match_file(alpm_handle_t *handle, struct _alpm_tri || (t->op & ALPM_HOOK_OP_UPGRADE && upgrade) || (t->op & ALPM_HOOK_OP_REMOVE && remove);
- alpm_list_free(install); - alpm_list_free(upgrade); - alpm_list_free(remove); + if(hook->needs_targets) { +#define _save_matches(_op, _matches) \ + if(t->op & _op && _matches) { \ + hook->matches = alpm_list_join(hook->matches, _matches); \ + } else { \ + alpm_list_free(_matches); \ + } + _save_matches(ALPM_HOOK_OP_INSTALL, install); + _save_matches(ALPM_HOOK_OP_UPGRADE, upgrade); + _save_matches(ALPM_HOOK_OP_REMOVE, remove); +#undef _save_matches + } else { + alpm_list_free(install); + alpm_list_free(upgrade); + alpm_list_free(remove); + }
return ret; }
OK (although I am not a big fan of defines used like that).
-static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, struct _alpm_trigger_t *t) +static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, + struct _alpm_hook_t *hook, struct _alpm_trigger_t *t) { + alpm_list_t *install = NULL, *upgrade = NULL, *remove = NULL; + if(t->op & ALPM_HOOK_OP_INSTALL || t->op & ALPM_HOOK_OP_UPGRADE) { alpm_list_t *i; for(i = handle->trans->add; i; i = i->next) { @@ -438,11 +459,19 @@ static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, struct _alpm_trig if(_alpm_fnmatch_patterns(t->targets, pkg->name) == 0) { if(alpm_db_get_pkg(handle->db_local, pkg->name)) { if(t->op & ALPM_HOOK_OP_UPGRADE) { - return 1; + if(hook->needs_targets) { + upgrade = alpm_list_add(upgrade, pkg->name); + } else { + return 1; + } } } else { if(t->op & ALPM_HOOK_OP_INSTALL) { - return 1; + if(hook->needs_targets) { + install = alpm_list_add(install, pkg->name); + } else { + return 1; + } } } } @@ -455,31 +484,47 @@ static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle, struct _alpm_trig alpm_pkg_t *pkg = i->data; if(pkg && _alpm_fnmatch_patterns(t->targets, pkg->name) == 0) { if(!alpm_list_find(handle->trans->add, pkg, _alpm_pkg_cmp)) { - return 1; + if(hook->needs_targets) { + remove = alpm_list_add(remove, pkg->name); + } else { + return 1; + } } } } }
- return 0; + /* if we reached this point we either need the target lists or we didn't + * match anything and the following calls will all be no-ops */ + hook->matches = alpm_list_join(hook->matches, install); + hook->matches = alpm_list_join(hook->matches, upgrade); + hook->matches = alpm_list_join(hook->matches, remove); + + return install || upgrade || remove; }
OK
-static int _alpm_hook_trigger_match(alpm_handle_t *handle, struct _alpm_trigger_t *t) +static int _alpm_hook_trigger_match(alpm_handle_t *handle, + struct _alpm_hook_t *hook, struct _alpm_trigger_t *t) { return t->type == ALPM_HOOK_TYPE_PACKAGE - ? _alpm_hook_trigger_match_pkg(handle, t) - : _alpm_hook_trigger_match_file(handle, t); + ? _alpm_hook_trigger_match_pkg(handle, hook, t) + : _alpm_hook_trigger_match_file(handle, hook, t); }
OK
static int _alpm_hook_triggered(alpm_handle_t *handle, struct _alpm_hook_t *hook) { alpm_list_t *i; + int ret = 0; for(i = hook->triggers; i; i = i->next) { - if(_alpm_hook_trigger_match(handle, i->data)) { - return 1; + if(_alpm_hook_trigger_match(handle, hook, i->data)) { + if(!hook->needs_targets) { + return 1; + } else { + ret = 1; + } } } - return 0; + return ret; }
OK
static int _alpm_hook_cmp(struct _alpm_hook_t *h1, struct _alpm_hook_t *h2) @@ -499,6 +544,44 @@ static alpm_list_t *find_hook(alpm_list_t *haystack, const void *needle) return NULL; }
+static ssize_t _alpm_hook_feed_targets(char *buf, ssize_t needed, alpm_list_t **pos) +{ + size_t remaining = needed, written = 0;; + size_t len; + + while(*pos && (len = strlen((*pos)->data)) + 1 <= remaining) { + memcpy(buf, (*pos)->data, len); + buf[len++] = '\n'; + *pos = (*pos)->next; + buf += len; + remaining -= len; + written += len; + } + + if(*pos && remaining) { + memcpy(buf, (*pos)->data, remaining); + (*pos)->data = (char*) (*pos)->data + remaining; + written += remaining; + } + + return written; +} +
OK
+static alpm_list_t *_alpm_strlist_dedup(alpm_list_t *list) +{ + alpm_list_t *i = list; + while(i) { + alpm_list_t *next = i->next; + while(next && strcmp(i->data, next->data) == 0) { + list = alpm_list_remove_item(list, next); + free(next); + next = i->next; + } + i = next; + } + return list; +} +
OK.
static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) { alpm_list_t *i, *pkgs = _alpm_db_get_pkgcache(handle->db_local); @@ -511,7 +594,16 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) } }
- return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, NULL, NULL); + if(hook->needs_targets) { + alpm_list_t *ctx; + hook->matches = alpm_list_msort(hook->matches, + alpm_list_count(hook->matches), (alpm_list_fn_cmp)strcmp); + ctx = hook->matches = _alpm_strlist_dedup(hook->matches);
Where do the duplicates come from?
+ return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, + (_alpm_cb_io) _alpm_hook_feed_targets, &ctx); + } else { + return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, NULL, NULL); + } }
int _alpm_hook_run(alpm_handle_t *handle, enum _alpm_hook_when_t when)
On 27/10/15 14:47, Andrew Gregory wrote:
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- lib/libalpm/hook.c | 8 ++++++++ 1 file changed, 8 insertions(+)
OK.
participants (2)
-
Allan McRae
-
Andrew Gregory