[pacman-dev] [PATCH 2/3] Add support for global hooks in /etc/pacman.d/hooks/*
Daniel Mendler
mail at daniel-mendler.de
Sun Jan 30 18:16:10 EST 2011
* The following hook functions are called if they exist:
pre_remove(pkgname, version)
post_remove(pkgname, version)
pre_upgrade(pkgname, ver, oldver)
post_upgrade(pkgname, ver, oldver)
pre_install(pkgname, ver)
post_install(pkgname, ver)
* The hook directory is configurable.
Signed-off-by: Daniel Mendler <mail at daniel-mendler.de>
---
etc/pacman.conf.in | 1 +
lib/libalpm/add.c | 28 +++++++++++++++++++++
lib/libalpm/alpm.h | 5 +++-
lib/libalpm/handle.c | 50 ++++++++++++++++++++++++++++++++++++++
lib/libalpm/handle.h | 1 +
lib/libalpm/remove.c | 12 +++++++++
lib/libalpm/trans.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/libalpm/trans.h | 1 +
src/pacman/Makefile.am | 2 +
src/pacman/conf.h | 3 ++
src/pacman/pacman.c | 30 ++++++++++++++++++++++
11 files changed, 195 insertions(+), 1 deletions(-)
diff --git a/etc/pacman.conf.in b/etc/pacman.conf.in
index 1105db9..95f6c5a 100644
--- a/etc/pacman.conf.in
+++ b/etc/pacman.conf.in
@@ -13,6 +13,7 @@
#DBPath = @localstatedir@/lib/pacman/
#CacheDir = @localstatedir@/cache/pacman/pkg/
#LogFile = @localstatedir@/log/pacman.log
+#HookDir = @sysconfdir@/pacman.d/hooks/
HoldPkg = pacman glibc
# If upgrades are available for these packages they will be asked for first
SyncFirst = pacman
diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c
index c37198c..498f6f2 100644
--- a/lib/libalpm/add.c
+++ b/lib/libalpm/add.c
@@ -519,6 +519,12 @@ static int commit_single_pkg(pmpkg_t *newpkg, size_t pkg_current,
_alpm_runscriptlet(handle->root, newpkg->origin_data.file,
"pre_upgrade", newpkg->version, oldpkg->version);
}
+
+ /* pre_upgrade hooks */
+ if(!(trans->flags & PM_TRANS_FLAG_NOHOOKS)) {
+ _alpm_runhooks(handle->root, handle->hookdir, "pre_upgrade",
+ newpkg->name, newpkg->version, oldpkg->version, NULL);
+ }
} else {
is_upgrade = 0;
@@ -531,6 +537,12 @@ static int commit_single_pkg(pmpkg_t *newpkg, size_t pkg_current,
_alpm_runscriptlet(handle->root, newpkg->origin_data.file,
"pre_install", newpkg->version, NULL);
}
+
+ /* pre_install hooks */
+ if(!(trans->flags & PM_TRANS_FLAG_NOHOOKS)) {
+ _alpm_runhooks(handle->root, handle->hookdir, "pre_install",
+ newpkg->name, newpkg->version, NULL);
+ }
}
/* we override any pre-set reason if we have alldeps or allexplicit set */
@@ -705,6 +717,22 @@ static int commit_single_pkg(pmpkg_t *newpkg, size_t pkg_current,
}
}
+ /* run the post-install hook */
+ if(!(trans->flags & PM_TRANS_FLAG_NOHOOKS)) {
+ if(is_upgrade) {
+ _alpm_runhooks(handle->root, handle->hookdir, "post_upgrade",
+ alpm_pkg_get_name(newpkg),
+ alpm_pkg_get_version(newpkg),
+ oldpkg ? alpm_pkg_get_version(oldpkg) : NULL,
+ NULL);
+ } else {
+ _alpm_runhooks(handle->root, handle->hookdir, "post_install",
+ alpm_pkg_get_name(newpkg),
+ alpm_pkg_get_version(newpkg),
+ NULL);
+ }
+ }
+
if(is_upgrade) {
EVENT(trans, PM_TRANS_EVT_UPGRADE_DONE, newpkg, oldpkg);
} else {
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 48a99d2..ecd295f 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -126,6 +126,9 @@ int alpm_option_set_logfile(const char *logfile);
const char *alpm_option_get_lockfile(void);
/* no set_lockfile, path is determined from dbpath */
+const char *alpm_option_get_hookdir(void);
+int alpm_option_set_hookdir(const char *hookdir);
+
int alpm_option_get_usesyslog(void);
void alpm_option_set_usesyslog(int usesyslog);
@@ -282,7 +285,7 @@ typedef enum _pmtransflag_t {
PM_TRANS_FLAG_DOWNLOADONLY = (1 << 9),
PM_TRANS_FLAG_NOSCRIPTLET = (1 << 10),
PM_TRANS_FLAG_NOCONFLICTS = (1 << 11),
- /* (1 << 12) flag can go here */
+ PM_TRANS_FLAG_NOHOOKS = (1 << 12),
PM_TRANS_FLAG_NEEDED = (1 << 13),
PM_TRANS_FLAG_ALLEXPLICIT = (1 << 14),
PM_TRANS_FLAG_UNNEEDED = (1 << 15),
diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
index 8872ed0..7457849 100644
--- a/lib/libalpm/handle.c
+++ b/lib/libalpm/handle.c
@@ -79,6 +79,7 @@ void _alpm_handle_free(pmhandle_t *handle)
FREELIST(handle->cachedirs);
FREE(handle->logfile);
FREE(handle->lockfile);
+ FREE(handle->hookdir);
FREE(handle->arch);
FREELIST(handle->dbs_sync);
FREELIST(handle->noupgrade);
@@ -169,6 +170,15 @@ const char SYMEXPORT *alpm_option_get_lockfile()
return handle->lockfile;
}
+const char SYMEXPORT *alpm_option_get_hookdir()
+{
+ if (handle == NULL) {
+ pm_errno = PM_ERR_HANDLE_NULL;
+ return NULL;
+ }
+ return handle->hookdir;
+}
+
int SYMEXPORT alpm_option_get_usesyslog()
{
if (handle == NULL) {
@@ -455,6 +465,46 @@ int SYMEXPORT alpm_option_set_logfile(const char *logfile)
return(0);
}
+int SYMEXPORT alpm_option_set_hookdir(const char *hookdir)
+{
+ struct stat st;
+ char *realhookdir;
+ size_t hookdirlen;
+
+ ALPM_LOG_FUNC;
+
+ if(!hookdir) {
+ pm_errno = PM_ERR_WRONG_ARGS;
+ return(-1);
+ }
+ if(stat(hookdir, &st) == -1 || !S_ISDIR(st.st_mode)) {
+ pm_errno = PM_ERR_NOT_A_DIR;
+ return(-1);
+ }
+
+ realhookdir = calloc(PATH_MAX+1, sizeof(char));
+ if(!realpath(hookdir, realhookdir)) {
+ FREE(realhookdir);
+ pm_errno = PM_ERR_NOT_A_DIR;
+ return(-1);
+ }
+
+ /* verify hookdir ends in a '/' */
+ hookdirlen = strlen(realhookdir);
+ if(realhookdir[hookdirlen-1] != '/') {
+ hookdirlen += 1;
+ }
+ if(handle->hookdir) {
+ FREE(handle->hookdir);
+ }
+ handle->hookdir = calloc(hookdirlen + 1, sizeof(char));
+ strncpy(handle->hookdir, realhookdir, hookdirlen);
+ handle->hookdir[hookdirlen-1] = '/';
+ FREE(realhookdir);
+ _alpm_log(PM_LOG_DEBUG, "option 'hookdir' = %s\n", handle->hookdir);
+ return(0);
+}
+
void SYMEXPORT alpm_option_set_usesyslog(int usesyslog)
{
handle->usesyslog = usesyslog;
diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h
index fa29d11..e728f76 100644
--- a/lib/libalpm/handle.h
+++ b/lib/libalpm/handle.h
@@ -48,6 +48,7 @@ typedef struct _pmhandle_t {
char *dbpath; /* Base path to pacman's DBs */
char *logfile; /* Name of the log file */
char *lockfile; /* Name of the lock file */
+ char *hookdir; /* Path of the hooks directory */
alpm_list_t *cachedirs; /* Paths to pacman cache directories */
/* package lists */
diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c
index 0641e43..be86a38 100644
--- a/lib/libalpm/remove.c
+++ b/lib/libalpm/remove.c
@@ -393,6 +393,12 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db)
alpm_pkg_get_version(info), NULL);
}
+ /* run the pre-remove hooks */
+ if(!(trans->flags & PM_TRANS_FLAG_NOHOOKS)) {
+ _alpm_runhooks(handle->root, handle->hookdir, "pre_remove",
+ pkgname, alpm_pkg_get_version(info), NULL);
+ }
+
if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) {
alpm_list_t *files = alpm_pkg_get_files(info);
alpm_list_t *newfiles;
@@ -438,6 +444,12 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db)
alpm_pkg_get_version(info), NULL);
}
+ /* run the post-remove hooks */
+ if(!(trans->flags & PM_TRANS_FLAG_NOHOOKS)) {
+ _alpm_runhooks(handle->root, handle->hookdir, "post_remove",
+ pkgname, alpm_pkg_get_version(info), NULL);
+ }
+
/* remove the package from the database */
_alpm_log(PM_LOG_DEBUG, "updating database\n");
_alpm_log(PM_LOG_DEBUG, "removing database entry '%s'\n", pkgname);
diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c
index 5b06505..95a6872 100644
--- a/lib/libalpm/trans.c
+++ b/lib/libalpm/trans.c
@@ -32,6 +32,7 @@
#include <sys/statvfs.h>
#include <errno.h>
#include <limits.h>
+#include <dirent.h>
/* libalpm */
#include "trans.h"
@@ -414,6 +415,68 @@ cleanup:
return(retval);
}
+int _alpm_runhooks(const char* root, const char *hookdir, const char* script, ...)
+{
+ char buf[PATH_MAX];
+ char argstr[PATH_MAX] = "";
+ char *argv[] = { "sh", "-c", buf, NULL };
+
+ ALPM_LOG_FUNC;
+
+ if(hookdir == NULL || access(hookdir, R_OK)) {
+ return(0);
+ }
+
+ /* join arguments */
+ va_list args;
+ va_start(args, script);
+ const char *arg;
+ char *p = argstr, *end = argstr + PATH_MAX - 1;
+ while ((arg = va_arg(args, const char*)) != NULL) {
+ size_t len = strlen(arg);
+ if (end - p < len + 1) {
+ break;
+ }
+ *p++ = ' ';
+ strcpy(p, arg);
+ p += len;
+ }
+ va_end(args);
+
+ /* read all directory entries (sorted) */
+ struct dirent **ent;
+ int num_ents = scandir(hookdir, &ent, NULL, alphasort);
+ if(num_ents < 0) {
+ _alpm_log(PM_LOG_ERROR, _("could not access hooks directory\n"));
+ return(1);
+ }
+
+ /* step through the directory entries */
+ int retval = 0, i;
+ for (i = 0; i < num_ents; ++i) {
+ if(strcmp(ent[i]->d_name, ".") != 0 && strcmp(ent[i]->d_name, "..") != 0) {
+ /* build the full filepath */
+ snprintf(buf, PATH_MAX, "%s%s", hookdir, ent[i]->d_name);
+
+ /* script found in file */
+ if(grep(buf, script)) {
+ snprintf(buf, PATH_MAX, ". %s%s; %s%s",
+ hookdir, ent[i]->d_name, script, argstr);
+
+ _alpm_log(PM_LOG_DEBUG, "executing \"%s\"\n", buf);
+ int ret = _alpm_run_chroot(root, "/bin/sh", argv);
+ if (ret != 0) {
+ retval = ret;
+ }
+ }
+ }
+ free(ent[i]);
+ }
+
+ free(ent);
+ return(retval);
+}
+
int SYMEXPORT alpm_trans_get_flags()
{
/* Sanity checks */
diff --git a/lib/libalpm/trans.h b/lib/libalpm/trans.h
index afe0ed7..832e919 100644
--- a/lib/libalpm/trans.h
+++ b/lib/libalpm/trans.h
@@ -75,6 +75,7 @@ int _alpm_trans_init(pmtrans_t *trans, pmtransflag_t flags,
int _alpm_runscriptlet(const char *root, const char *installfn,
const char *script, const char *ver,
const char *oldver);
+int _alpm_runhooks(const char* hooks, const char *hookdir, const char *script, ...);
#endif /* _ALPM_TRANS_H */
diff --git a/src/pacman/Makefile.am b/src/pacman/Makefile.am
index 31e8b13..335deee 100644
--- a/src/pacman/Makefile.am
+++ b/src/pacman/Makefile.am
@@ -1,5 +1,6 @@
# paths set at make time
conffile = ${sysconfdir}/pacman.conf
+hookdir = ${sysconfdir}/pacman.d/hooks/
dbpath = ${localstatedir}/lib/pacman/
cachedir = ${localstatedir}/cache/pacman/pkg/
logfile = ${localstatedir}/log/pacman.log
@@ -12,6 +13,7 @@ DEFS = -DLOCALEDIR=\"@localedir@\" \
-DDBPATH=\"$(dbpath)\" \
-DCACHEDIR=\"$(cachedir)\" \
-DLOGFILE=\"$(logfile)\" \
+ -DHOOKDIR=\"$(hookdir)\" \
@DEFS@
INCLUDES = -I$(top_srcdir)/lib/libalpm
diff --git a/src/pacman/conf.h b/src/pacman/conf.h
index ff7a9c7..03fe424 100644
--- a/src/pacman/conf.h
+++ b/src/pacman/conf.h
@@ -40,6 +40,7 @@ typedef struct __config_t {
char *rootdir;
char *dbpath;
char *logfile;
+ char *hookdir;
/* TODO how to handle cachedirs? */
unsigned short op_q_isfile;
@@ -98,10 +99,12 @@ enum {
OP_DEBUG,
OP_NOPROGRESSBAR,
OP_NOSCRIPTLET,
+ OP_NOHOOKS,
OP_ASK,
OP_CACHEDIR,
OP_ASDEPS,
OP_LOGFILE,
+ OP_HOOKDIR,
OP_IGNOREGROUP,
OP_NEEDED,
OP_ASEXPLICIT,
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index 45500cf..c0a4e36 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -189,6 +189,7 @@ static void usage(int op, const char * const myname)
addlist(_(" -k, --dbonly only modify database entries, not package files\n"));
addlist(_(" --noprogressbar do not show a progress bar when downloading files\n"));
addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n"));
+ addlist(_(" --nohooks do not execute the global hooks\n"));
addlist(_(" --print only print the targets instead of performing the operation\n"));
addlist(_(" --print-format <string>\n"
" specify how the targets should be printed\n"));
@@ -197,6 +198,7 @@ static void usage(int op, const char * const myname)
addlist(_(" -b, --dbpath <path> set an alternate database location\n"));
addlist(_(" -r, --root <path> set an alternate installation root\n"));
+ addlist(_(" --hookdir <path> set an alternate hooks directory\n"));
addlist(_(" -v, --verbose be verbose\n"));
addlist(_(" --arch <arch> set an alternate architecture\n"));
addlist(_(" --cachedir <dir> set an alternate package cache location\n"));
@@ -364,6 +366,11 @@ static void setlibpaths(void)
snprintf(path, PATH_MAX, "%s%s", alpm_option_get_root(), LOGFILE + 1);
config->logfile = strdup(path);
}
+ if(!config->hookdir) {
+ /* omit leading slash from our static LOGFILE path, root handles it */
+ snprintf(path, PATH_MAX, "%s%s", alpm_option_get_root(), HOOKDIR + 1);
+ config->hookdir = strdup(path);
+ }
}
/* Set other paths if they were configured. Note that unless rootdir
* was left undefined, these two paths (dbpath and logfile) will have
@@ -384,6 +391,14 @@ static void setlibpaths(void)
cleanup(ret);
}
}
+ if(config->hookdir) {
+ ret = alpm_option_set_hookdir(config->hookdir);
+ if(ret != 0) {
+ pm_printf(PM_LOG_ERROR, _("problem setting hookdir '%s' (%s)\n"),
+ config->hookdir, alpm_strerrorlast());
+ cleanup(ret);
+ }
+ }
/* add a default cachedir if one wasn't specified */
if(alpm_option_get_cachedirs() == NULL) {
@@ -505,6 +520,10 @@ static int parsearg_global(int opt)
config->logfile = strndup(optarg, PATH_MAX);
break;
case OP_NOCONFIRM: config->noconfirm = 1; break;
+ case OP_HOOKDIR:
+ check_optarg();
+ config->hookdir = strdup(optarg);
+ break;
case 'b':
check_optarg();
config->dbpath = strdup(optarg);
@@ -556,6 +575,7 @@ static int parsearg_trans(int opt)
case 'k': config->flags |= PM_TRANS_FLAG_DBONLY; break;
case OP_NOPROGRESSBAR: config->noprogressbar = 1; break;
case OP_NOSCRIPTLET: config->flags |= PM_TRANS_FLAG_NOSCRIPTLET; break;
+ case OP_NOHOOKS: config->flags |= PM_TRANS_FLAG_NOHOOKS; break;
case 'p': config->print = 1; break;
case OP_PRINTFORMAT:
check_optarg();
@@ -680,12 +700,14 @@ static int parseargs(int argc, char *argv[])
{"verbose", no_argument, 0, 'v'},
{"downloadonly", no_argument, 0, 'w'},
{"refresh", no_argument, 0, 'y'},
+ {"hookdir", required_argument, 0, OP_HOOKDIR},
{"noconfirm", no_argument, 0, OP_NOCONFIRM},
{"config", required_argument, 0, OP_CONFIG},
{"ignore", required_argument, 0, OP_IGNORE},
{"debug", optional_argument, 0, OP_DEBUG},
{"noprogressbar", no_argument, 0, OP_NOPROGRESSBAR},
{"noscriptlet", no_argument, 0, OP_NOSCRIPTLET},
+ {"nohooks", no_argument, 0, OP_NOHOOKS},
{"ask", required_argument, 0, OP_ASK},
{"cachedir", required_argument, 0, OP_CACHEDIR},
{"asdeps", no_argument, 0, OP_ASDEPS},
@@ -1003,6 +1025,12 @@ static int _parse_options(char *key, char *value)
config->logfile = strdup(value);
pm_printf(PM_LOG_DEBUG, "config: logfile: %s\n", value);
}
+ } else if(strcmp(key, "HookDir") == 0) {
+ /* don't overwrite a path specified on the command line */
+ if(!config->hookdir) {
+ config->hookdir = strdup(value);
+ pm_printf(PM_LOG_DEBUG, "config: hookdir: %s\n", value);
+ }
} else if (strcmp(key, "XferCommand") == 0) {
config->xfercommand = strdup(value);
alpm_option_set_fetchcb(download_with_xfercommand);
@@ -1334,6 +1362,7 @@ int main(int argc, char *argv[])
/* define paths to reasonable defaults */
alpm_option_set_root(ROOTDIR);
alpm_option_set_dbpath(DBPATH);
+ alpm_option_set_hookdir(HOOKDIR);
alpm_option_set_logfile(LOGFILE);
/* Priority of options:
@@ -1429,6 +1458,7 @@ int main(int argc, char *argv[])
printf("\n");
printf("Lock File : %s\n", alpm_option_get_lockfile());
printf("Log File : %s\n", alpm_option_get_logfile());
+ printf("Hook Dir : %s\n", alpm_option_get_hookdir());
list_display("Targets :", pm_targets);
}
--
1.7.3.5
More information about the pacman-dev
mailing list