[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