--- src/pacman/Makefile.am | 1 + src/pacman/callback.c | 39 +++++++++++++ src/pacman/hook.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++ src/pacman/hook.h | 52 +++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 src/pacman/hook.c create mode 100644 src/pacman/hook.h diff --git a/src/pacman/Makefile.am b/src/pacman/Makefile.am index 5f10308..1834e3e 100644 --- a/src/pacman/Makefile.am +++ b/src/pacman/Makefile.am @@ -31,6 +31,7 @@ pacman_SOURCES = \ conf.h conf.c \ database.c \ deptest.c \ + hook.h hook.c \ ini.h ini.c \ package.h package.c \ pacman.h pacman.c \ diff --git a/src/pacman/callback.c b/src/pacman/callback.c index 340a3a1..35ff077 100644 --- a/src/pacman/callback.c +++ b/src/pacman/callback.c @@ -34,6 +34,7 @@ #include "pacman.h" #include "callback.h" #include "util.h" +#include "hook.h" #include "conf.h" /* download progress bar */ @@ -148,6 +149,35 @@ static void fill_progress(const int bar_percent, const int disp_percent, fflush(stdout); } +static int run_hooks(pu_hook_when_t when) +{ + alpm_list_t *i, *hooks = NULL, *files = pu_hook_find_files(); + int ret = 0; + for(i = files; i; i = alpm_list_next(i)) { + pu_hook_t *hook = pu_hook_load_file(i->data); + if(hook) { + hooks = alpm_list_add(hooks, hook); + } else { + ret = 1; + } + } + + for(i = hooks; i; i = alpm_list_next(i)) { + pu_hook_t *hook = i->data; + if(hook->when == when + && pu_hook_match_transaction(hook, config->handle) + && pu_hook_run(hook) != 0) { + ret = 1; + } + } + + FREELIST(files); + /*alpm_list_free_inner(pu_hook_free);*/ + alpm_list_free(hooks); + + return ret; +} + /* callback to handle messages/notifications from libalpm transactions */ void cb_event(alpm_event_t *event) { @@ -336,6 +366,15 @@ void cb_event(alpm_event_t *event) } } break; + case ALPM_EVENT_TRANS_COMMIT_START: + if(run_hooks(PU_HOOK_WHEN_BEFORE_TRANSACTION) != 0) { + printf("interrupting transaction...\n"); + alpm_trans_interrupt(config->handle); + } + break; + case ALPM_EVENT_TRANS_COMMIT_END: + run_hooks(PU_HOOK_WHEN_AFTER_TRANSACTION); + break; /* all the simple done events, with fallthrough for each */ case ALPM_EVENT_FILECONFLICTS_DONE: case ALPM_EVENT_CHECKDEPS_DONE: diff --git a/src/pacman/hook.c b/src/pacman/hook.c new file mode 100644 index 0000000..34c2f62 --- /dev/null +++ b/src/pacman/hook.c @@ -0,0 +1,156 @@ +#include "hook.h" +#include "ini.h" + +#include <fnmatch.h> +#include <string.h> +#include <dirent.h> + +#define FNMATCH(pattern, string) fnmatch(pattern, string, 0) + +alpm_list_t *pu_hook_find_files(void) +{ + alpm_list_t *files = NULL; + DIR *dd = opendir(PU_HOOK_USER_DIR); + struct dirent entry, *result; + if(!dd) { + return NULL; + } + + while(readdir_r(dd, &entry, &result) == 0 && result) { + char *path = malloc(strlen(PU_HOOK_USER_DIR) + strlen(entry.d_name) + 2); + sprintf(path, "%s/%s", PU_HOOK_USER_DIR, entry.d_name); + files = alpm_list_add(files, path); + } + + return files; +} + +static int _filelist_contains_glob(alpm_filelist_t *files, const char *glob) +{ + unsigned int i; + for(i = 0; i < files->count; ++i) { + if(FNMATCH(glob, files->files[i].name) == 0) { + return 1; + } + } + return 0; +} + +static int _pu_hook_trigger_match_packages(pu_trigger_t *trigger, alpm_list_t *pkgs) +{ + alpm_list_t *i; + for(i = pkgs; i; i = alpm_list_next(i)) { + alpm_pkg_t *p = i->data; + switch(trigger->target) { + case PU_TRIGGER_OBJECT_PACKAGE: + if(FNMATCH(trigger->glob, alpm_pkg_get_name(p)) == 0) { + return 1; + } + break; + case PU_TRIGGER_OBJECT_FILE: + if(_filelist_contains_glob(alpm_pkg_get_files(p), trigger->glob)) { + return 1; + } + break; + } + } + return 0; +} + +static int _pu_hook_trigger_match_transaction(pu_trigger_t *trigger, alpm_handle_t *handle) +{ + switch(trigger->operation) { + case PU_TRIGGER_OPERATION_INSTALL: + return _pu_hook_trigger_match_packages(trigger, alpm_trans_get_add(handle)); + break; + case PU_TRIGGER_OPERATION_REMOVE: + return _pu_hook_trigger_match_packages(trigger, alpm_trans_get_remove(handle)); + break; + } + return 0; +} + +int pu_hook_match_transaction(pu_hook_t *hook, alpm_handle_t *handle) +{ + alpm_list_t *i; + for(i = hook->triggers; i; i = alpm_list_next(i)) { + if(_pu_hook_trigger_match_transaction(i->data, handle)) { + return 1; + } + } + return 0; +} + +int pu_hook_run(pu_hook_t *hook) +{ + return system(hook->cmd); +} + +static int _parse_hook(const char __attribute__((unused)) *file, + int __attribute__((unused)) linenum, const char *section, + char *key, char *val, void *data) +{ + pu_hook_t *hook = data; + + if(section && !key && !val) { + if(strcmp(section, "Trigger") == 0) { + hook->triggers = alpm_list_add(hook->triggers, calloc(sizeof(pu_trigger_t), 1)); + } + } + + if(!section || !key || !val) { + return 0; + } + + if(strcmp(section, "Trigger") == 0) { + pu_trigger_t *trig = alpm_list_last(hook->triggers)->data; + if(strcmp(key, "Object") == 0) { + if(strcmp(val, "File") == 0) { + trig->target = PU_TRIGGER_OBJECT_FILE; + } else if(strcmp(val, "Package") == 0) { + trig->target = PU_TRIGGER_OBJECT_PACKAGE; + } else { + fprintf(stderr, "unknown object '%s'\n", val); + } + } else if(strcmp(key, "Operation") == 0) { + if(strcmp(val, "Install") == 0) { + trig->operation = PU_TRIGGER_OPERATION_INSTALL; + } else if(strcmp(val, "Remove") == 0) { + trig->operation = PU_TRIGGER_OPERATION_REMOVE; + } else { + fprintf(stderr, "unknown action '%s'\n", val); + } + } else if(strcmp(key, "Target") == 0) { + trig->glob = strdup(val); + } else { + fprintf(stderr, "unknown key '%s'\n", key); + } + } else if(strcmp(section, "Action") == 0) { + if(strcmp(key, "When") == 0) { + if(strcmp(val, "PreTransaction") == 0) { + hook->when = PU_HOOK_WHEN_BEFORE_TRANSACTION; + } else if(strcmp(val, "PostTransaction") == 0) { + hook->when = PU_HOOK_WHEN_AFTER_TRANSACTION; + } else { + fprintf(stderr, "unknown When '%s'\n", val); + } + } else if(strcmp(key, "Exec") == 0) { + hook->cmd = strdup(val); + } else { + fprintf(stderr, "unknown key '%s'\n", key); + } + } else { + fprintf(stderr, "unknown section '%s'\n", section); + } + + return 0; +} + +pu_hook_t *pu_hook_load_file(const char *path) +{ + pu_hook_t *hook = calloc(sizeof(pu_hook_t), 1); + parse_ini(path, _parse_hook, hook); + return hook; +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/hook.h b/src/pacman/hook.h new file mode 100644 index 0000000..812a9a4 --- /dev/null +++ b/src/pacman/hook.h @@ -0,0 +1,52 @@ +#ifndef PU_HOOK_H +#define PU_HOOK_H + +#include <alpm.h> + +#ifndef PU_HOOK_SYS_DIR +#define PU_HOOK_SYS_DIR "/usr/lib/pacutils/hooks/" +#endif + +#ifndef PU_HOOK_USER_DIR +#define PU_HOOK_USER_DIR "/etc/pacman.d/hooks/" +#endif + +typedef enum { + PU_HOOK_WHEN_BEFORE_TRANSACTION, + PU_HOOK_WHEN_AFTER_TRANSACTION, +} pu_hook_when_t; + +typedef enum { + PU_TRIGGER_OBJECT_PACKAGE, + PU_TRIGGER_OBJECT_FILE, +} pu_hook_trigger_object_t; + +typedef enum { + PU_TRIGGER_OPERATION_INSTALL, + PU_TRIGGER_OPERATION_REMOVE, +} pu_hook_trigger_operation_t; + +typedef struct { + pu_hook_trigger_object_t target; + pu_hook_trigger_operation_t operation; + char *glob; +} pu_trigger_t; + +typedef struct { + char *name; + pu_hook_when_t when; + alpm_list_t *triggers; + char *cmd; +} pu_hook_t; + +alpm_list_t *pu_hook_find_files(void); + +pu_hook_t *pu_hook_load_file(const char *path); + +int pu_hook_run(pu_hook_t *hook); + +int pu_hook_match_transaction(pu_hook_t *hooks, alpm_handle_t *handle); + +#endif /* PU_HOOK_H */ + +/* vim: set ts=2 sw=2 noet: */ -- 1.9.1