[pacman-dev] [PATCH 2/2][WIP][RFC] add basic hook support
Andrew Gregory
andrew.gregory.8 at gmail.com
Thu Apr 10 19:59:53 EDT 2014
---
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
More information about the pacman-dev
mailing list