---
 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

Reply via email to