Юрий Седунов просил меня реализовать posttrans filetriggers, чтобы решить проблему c gtk icon cache в branch-4.1 (для предостящего релиза дистрибутива). Проблема это такая: библиотека libgtk+2 может использовать кеш иконок /usr/share/icons/hicolor/icon-theme.cache, чтобы ускорить загрузку иконок. По умолчанию этот кеш отсутствует; но если он создан, то libgtk+2 не находит иконки, отсутствующие в кеше (но существующие в файловой системе). Пакет NetworkManager-gnome создаёт/обновляет этот кеш, а большая часть других пакетов (около 700+ штук) не создаёт и не обновляет этот кеш. Это означает, что после установки NetworkManager-gnome библиотека libgtk+2 будет использовать кеш, созданный на стадии установки NetworkManager-gnome, а установка дополнительных пакетов с иконками на этот кеш никак не повлияет, то есть, в конечном счёте, в приложениях будут битые иконки. Возможное решение проблемы -- добавить %post-скрипт для обновления кеша иконок во все пакеты, которые содержат /usr/share/icons/hicolor/*. Возражения: 1) Пачить 700+ штук пакетов -- мартышкин труд. 2) Пакеты с иконками не обязаны знать специфику работы libgtk+2 кеша, и даже что он вообще есть. 3) Можно сэкономить время на установке, если создать/обновить кеш только один раз, в конце транзакции. Идея posttrans filetriggers простая -- во время выполнения транзакции создаётся список установленных/удалённых файлов. Дальше можно написать скрипт, который, если транзакция успешно завершилась, получит на вход этот список файлов и сможет выполнить определённые действия. Упрощенный пример для обновления кеша иконок. /usr/lib/rpm/gtk+2-icon-cache.filetrigger: #!/bin/sh if grep -qs ^/usr/share/icons/hicolor/; then gtk-update-icon-cache --force --ignore-theme-index fi Упрощенный пример для регистрации GConf2 схем. /usr/lib/rpm/GConf2-schemas.filetrigger: #!/bin/sh install= remove= while read -r f; do case "$f" in /etc/gconf/schemas/*.schemas) ;; *) continue ;; esac if [ -f "$f" ]; then install="$install $f" else remove="$remove $f" fi done [ -z "$install" ] || gconftool-2 --makefile-install-rule $install [ -z "$remove" ] || gconftool-2 --makefile-uninstall-rule $remove Последний пример показывает, что можно реализовать достаточно сложные случаи обработки аргументов, накапливая аргументы в цикле (чтобы запустить прогрмму всего одни раз). На самом деле в реальном скрипте лучше использовать xargs --delimiter='\n', потому что argv может переполниться, а файлы могут содержать пробелы). В отличие от мандривовского патча, я не стал делать у файлов префиксы "+" и "-", т.к. из-за особенностей librpm нельзя сделать чтобы это хорошо работало (т.е. "+" может и не означать, что файл был добавлен, а "-" может не означать, что файл таки был удалён). В последнем примере я просто явно проверяю [ -f "$f" ]. Changelog since common ancestor `4.0.4-alt95.M41.2' follows: commit 462bd2a2b95b6946ccd347b8d1848943d5ae3c99 Author: Alexey Tourbin Date: Mon Sep 8 10:57:19 2008 +0400 implemented posttrans filetriggers, vaguely based on Mandriva patch Full diff since common ancestor `4.0.4-alt95.M41.2' follows: diff --git a/a b/a new file mode 100644 index 0000000..e69de29 diff --git a/configure.in b/configure.in index 2e7b53e..82a959c 100644 --- a/configure.in +++ b/configure.in @@ -1022,6 +1022,7 @@ AC_OUTPUT([ Doxyfile Makefile rpmrc macros platform rpmpopt rpm.spec scripts/strip_files scripts/symlinks.req scripts/verify-elf + scripts/posttrans-filetriggers tests/Makefile tests/rpmrc tests/macros tests/hello-test/Makefile po/Makefile.in doc/Makefile doc/manual/Makefile diff --git a/lib/psm.c b/lib/psm.c index b9e6f8e..3ccaf60 100644 --- a/lib/psm.c +++ b/lib/psm.c @@ -900,7 +900,7 @@ static int runScript(PSM_t psm, Header h, int freePrefixes = 0; FD_t out; rpmRC rc = RPMRC_OK; - const char *n, *v, *r; + const char *n = NULL, *v = NULL, *r = NULL; char arg1_str [sizeof(int)*3+1] = ""; char arg2_str [sizeof(int)*3+1] = ""; @@ -917,6 +917,7 @@ static int runScript(PSM_t psm, Header h, argc = progArgc; } + if (h) xx = headerNVR(h, &n, &v, &r); if (arg1 >= 0) @@ -924,9 +925,9 @@ static int runScript(PSM_t psm, Header h, if (arg2 >= 0) sprintf(arg2_str, "%d", arg2); - if (hge(h, RPMTAG_INSTPREFIXES, &ipt, (void **) &prefixes, &numPrefixes)) { + if (h && hge(h, RPMTAG_INSTPREFIXES, &ipt, (void **) &prefixes, &numPrefixes)) { freePrefixes = 1; - } else if (hge(h, RPMTAG_INSTALLPREFIX, NULL, (void **) &oldPrefix, NULL)) { + } else if (h && hge(h, RPMTAG_INSTALLPREFIX, NULL, (void **) &oldPrefix, NULL)) { prefixes = &oldPrefix; numPrefixes = 1; } else { @@ -1038,6 +1039,7 @@ static int runScript(PSM_t psm, Header h, } } + if (n) dosetenv ("RPM_INSTALL_NAME", n, 1); if (*arg1_str) @@ -2130,3 +2132,58 @@ fprintf(stderr, "*** PSM_RDB_LOAD: header #%u not found\n", fi->record); return rc; /*@=nullstate@*/ } + +static +void saveTriggerFiles(PSM_t psm) +{ + const rpmTransactionSet ts = psm->ts; + if (ts->transFlags & RPMTRANS_FLAG_TEST) + return; + if (ts->transFlags & (_noTransScripts | _noTransTriggers)) + return; + const TFI_t fi = psm->fi; + if (fi->fc < 1) + return; + psmStage(psm, PSM_CHROOT_IN); + const char *file = rpmGetPath(ts->rpmdb->db_home, "/files-awaiting-filetriggers"); + FILE *fp = fopen(file, "a"); + if (fp == NULL) + rpmError(RPMERR_OPEN, "open of %s failed: %s\n", file, strerror(errno)); + else { + int i; + for (i = 0; i < fi->fc; i++) + fprintf(fp, "%s%s\n", fi->dnl[fi->dil[i]], fi->bnl[i]); + fclose(fp); + } + file = _free(file); + psmStage(psm, PSM_CHROOT_OUT); +} + +void psmTriggerAdded(PSM_t psm) +{ + saveTriggerFiles(psm); +} + +void psmTriggerRemoved(PSM_t psm) +{ + saveTriggerFiles(psm); +} + +void psmTriggerPosttrans(PSM_t psm) +{ + const rpmTransactionSet ts = psm->ts; + if (ts->transFlags & RPMTRANS_FLAG_TEST) + return; + if (ts->transFlags & (_noTransScripts | _noTransTriggers)) + return; + psmStage(psm, PSM_CHROOT_IN); + const char *file = rpmGetPath(ts->rpmdb->db_home, "/files-awaiting-filetriggers"); + const char *script = RPMCONFIGDIR "/posttrans-filetriggers"; + const char *argv[] = { script, file, NULL }; + rpmMessage(RPMMESS_VERBOSE, _("Running %s\n"), script); + int rc = runScript(psm, NULL, script, 2, argv, NULL, 0, 0); + if (rc == 0) + unlink(file); + file = _free(file); + psmStage(psm, PSM_CHROOT_OUT); +} diff --git a/lib/psm.h b/lib/psm.h index 968178b..90f1419 100644 --- a/lib/psm.h +++ b/lib/psm.h @@ -233,6 +233,11 @@ int psmStage(PSM_t psm, pkgStage stage) /*@modifies psm, rpmGlobalMacroContext, fileSystem, internalState @*/; +/* ALT: hack to implement file triggers */ +void psmTriggerAdded(PSM_t psm); +void psmTriggerRemoved(PSM_t psm); +void psmTriggerPosttrans(PSM_t psm); + #ifdef __cplusplus } #endif diff --git a/lib/transaction.c b/lib/transaction.c index 2073882..ade1c46 100644 --- a/lib/transaction.c +++ b/lib/transaction.c @@ -2068,6 +2068,9 @@ assert(alp == fi->ap); ourrc++; lastFailed = i; } + else { + psmTriggerAdded(psm); + } fi->h = headerFree(fi->h); if (hsave) { fi->h = headerLink(hsave); @@ -2094,11 +2097,18 @@ assert(alp == fi->ap); if (psmStage(psm, PSM_PKGERASE)) ourrc++; + else { + psmTriggerRemoved(psm); + } break; } (void) rpmdbSync(ts->rpmdb); } + + if (ourrc == 0) + psmTriggerPosttrans(psm); + tsi = tsFreeIterator(tsi); ts->flList = freeFl(ts, ts->flList); diff --git a/rpm-4_0.spec b/rpm-4_0.spec index 46c08b8..435831f 100644 --- a/rpm-4_0.spec +++ b/rpm-4_0.spec @@ -291,6 +291,7 @@ for dbi in \ do touch "%buildroot%_localstatedir/%name/$dbi" done +touch %buildroot%_localstatedir/%name/files-awaiting-filetriggers # Prepare documentation. bzip2 -9 CHANGES ||: @@ -444,6 +445,7 @@ fi %rpmdbattr %_localstatedir/%name/Sigmd5 %rpmdbattr %_localstatedir/%name/Sha1header %rpmdbattr %_localstatedir/%name/Triggername +%rpmdbattr %_localstatedir/%name/files-awaiting-filetriggers /bin/rpm %_bindir/rpm @@ -465,6 +467,8 @@ fi %_prefix/lib/rpmpopt %_prefix/lib/rpmrc +%rpmattr %_rpmlibdir/posttrans-filetriggers + %rpmattr %_rpmlibdir/functions %rpmattr %_rpmlibdir/find-package %rpmdatattr %_rpmlibdir/.provides.sh diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 6d5a5c1..b9900ba 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -3,6 +3,7 @@ AUTOMAKE_OPTIONS = 1.4 foreign EXTRA_DIST = \ + posttrans-filetriggers \ functions find-package .provides.sh \ find-scriptlet-requires \ brp-adjust_libraries brp-alt brp-bytecompile_python \ @@ -32,6 +33,7 @@ all: configdir = ${prefix}/lib/rpm config_DATA = .provides.sh 0common-files.req.list config_SCRIPTS = \ + posttrans-filetriggers \ functions find-package \ find-scriptlet-requires \ brp-adjust_libraries brp-alt brp-bytecompile_python \ diff --git a/scripts/posttrans-filetriggers.in b/scripts/posttrans-filetriggers.in new file mode 100755 index 0000000..1f80c51 --- /dev/null +++ b/scripts/posttrans-filetriggers.in @@ -0,0 +1,32 @@ +#!/bin/sh -efu +# +# File triggers are run at the end of successful transaction. +# +# Copyright (C) 2008 Alexey Tourbin +# +# Vaguely based on filetriggers.patch from Mandriva Linux. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +filelist=$1 +shift + +RC=0 + +if [ -s "$filelist" ]; then + LC_ALL=C sort -u -o "$filelist" "$filelist" + set +f + for filetrigger in @RPMCONFIGDIR@/*.filetrigger; do + [ -x "$filetrigger" ] || continue + "$filetrigger" <"$filelist" || + { + echo >&2 "$filetrigger failed" + RC=1 + } + done +fi + +exit $RC