Implement file triggers (%transfiletriggerin) for layered pkgs

File triggers are a post-RHEL7 thing; more information at
http://rpm.org/user_doc/file_triggers.html

There are two notable users I've been testing this with;
`glib2` and `vagrant`.  The `vagrant` one is more immediately urgent,
since it makes `vagrant-libvirt` work, which I currently rely on
for my workstation dev.

I've tested things successfully with `vagrant`, and I did verify that we run the
`glib2` ones when doing `rpm-ostree ex container`.

Long term, more transaction file triggers are likely to live in
"base" packages like `glib2`.  We don't implement those yet, but
extending this to do that shouldn't be too hard.

There was *significant* what I'd call reverse engineering of the
implementation in librpm.  The file triggers code there is spread out
and abstracted in a few different places in the code.  I found
trying to understand what header values were involved to be quite
tricky.

There are some corner cases like multiple patterns that I *think*
this does correctly, but could use more validation.  The main
question I had was - is it required that the patterns for e.g.
`%transfiletriggerin` and `%transfiletriggerun` be identical?

Closes: https://github.com/projectatomic/rpm-ostree/issues/648

Closes: #869
Approved by: jlebon
This commit is contained in:
Colin Walters 2017-07-07 10:45:10 -04:00 committed by Atomic Bot
parent d4effe8f47
commit 0d4d6be94f
7 changed files with 442 additions and 30 deletions

View File

@ -83,17 +83,6 @@ CAP_LIBS="$LIBS"
AC_SUBST(CAP_LIBS)
LIBS="$save_LIBS"
AC_SEARCH_LIBS([rpmsqSetInterruptSafety], [rpmio],
AC_DEFINE([BUILDOPT_HAVE_RPMSQ_SET_INTERRUPT_SAFETY], 1, [Set to 1 if we have interrupt safety API]),
AC_DEFINE([BUILDOPT_HAVE_RPMSQ_SET_INTERRUPT_SAFETY], 0, [Set to 1 if we have interrupt safety API])
)
# rpmfiles was made public in rpm >= 4.12, el7 is still on 4.11
AC_SEARCH_LIBS([rpmfilesNew], [rpm],
AC_DEFINE([BUILDOPT_HAVE_RPMFILES], 1, [Set to 1 if we have rpmfiles API]),
AC_DEFINE([BUILDOPT_HAVE_RPMFILES], 0, [Set to 1 if we have rpmfiles API])
)
# Remember to update AM_CPPFLAGS in Makefile.am when bumping GIO req.
PKG_CHECK_MODULES(PKGDEP_GIO_UNIX, [gio-unix-2.0])
PKG_CHECK_MODULES(PKGDEP_RPMOSTREE, [gio-unix-2.0 >= 2.40.0 json-glib-1.0
@ -106,6 +95,14 @@ dnl bundled libdnf
PKGDEP_RPMOSTREE_CFLAGS="-I $(pwd)/libdnf -I $(pwd)/libdnf-build $PKGDEP_RPMOSTREE_CFLAGS"
PKGDEP_RPMOSTREE_LIBS="-L$(pwd)/libdnf-build/libdnf -ldnf $PKGDEP_RPMOSTREE_LIBS"
dnl This is the current version in Fedora 25.
AS_IF([pkg-config --atleast-version=4.13.0.1 rpm], [
have_modern_rpm=true
AC_DEFINE([BUILDOPT_HAVE_RPMSQ_SET_INTERRUPT_SAFETY], 1, [Set to 1 if we have interrupt safety API])
AC_DEFINE([BUILDOPT_HAVE_RPM_FILETRIGGERS], 1, [Set to 1 if we have file triggers])
AC_DEFINE([BUILDOPT_HAVE_RPMFILES], 1, [Set to 1 if we have rpmfiles API]),
], [have_modern_rpm=false])
AC_PATH_PROG([XSLTPROC], [xsltproc])
GLIB_TESTS
@ -209,9 +206,10 @@ AC_OUTPUT
echo "
$PACKAGE $VERSION
nts name: $enable_new_name
compose tooling: $enable_compose_tooling
introspection: $found_introspection
bubblewrap: $with_bubblewrap
gtk-doc: $enable_gtk_doc
built with modern RPM (e.g. Fedora 25+): $have_modern_rpm
nts name: $enable_new_name
compose tooling: $enable_compose_tooling
introspection: $found_introspection
bubblewrap: $with_bubblewrap
gtk-doc: $enable_gtk_doc
"

View File

@ -2708,6 +2708,45 @@ add_install (RpmOstreeContext *self,
return TRUE;
}
/* Run %transfiletriggerin */
static gboolean
run_all_transfiletriggers (RpmOstreeContext *self,
rpmts ts,
int rootfs_dfd,
GCancellable *cancellable,
GError **error)
{
/* Triggers from base packages */
g_auto(rpmdbMatchIterator) mi = rpmtsInitIterator (ts, RPMDBI_PACKAGES, NULL, 0);
{ Header hdr;
while ((hdr = rpmdbNextIterator (mi)) != NULL)
{
if (!rpmostree_transfiletriggers_run_sync (hdr, rootfs_dfd,
cancellable, error))
return FALSE;
}
}
/* Triggers from newly added packages */
const guint n = (guint)rpmtsNElements (ts);
for (guint i = 0; i < n; i++)
{
rpmte te = rpmtsElement (ts, i);
if (rpmteType (te) != TR_ADDED)
continue;
DnfPackage *pkg = (void*)rpmteKey (te);
g_autofree char *path = get_package_relpath (pkg);
g_auto(Header) hdr = NULL;
if (!get_package_metainfo (self, path, &hdr, NULL, error))
return FALSE;
if (!rpmostree_transfiletriggers_run_sync (hdr, rootfs_dfd,
cancellable, error))
return FALSE;
}
return TRUE;
}
gboolean
rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
int tmprootfs_dfd,
@ -2987,10 +3026,13 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
return FALSE;
}
if (!run_all_transfiletriggers (self, ordering_ts, tmprootfs_dfd,
cancellable, error))
return FALSE;
/* We want this to be the first error message if something went wrong
* with a script; see https://github.com/projectatomic/rpm-ostree/pull/888
*/
gboolean skip_sanity_check = FALSE;
g_variant_dict_lookup (self->spec->dict, "skip-sanity-check", "b", &skip_sanity_check);
if (!skip_sanity_check &&

View File

@ -29,3 +29,5 @@ bash.post, RPMOSTREE_SCRIPT_ACTION_TODO_SHELL_POSTTRANS
glibc-common.post, RPMOSTREE_SCRIPT_ACTION_TODO_SHELL_POSTTRANS
/* Seems to be another case of legacy workaround */
gdb.prein, RPMOSTREE_SCRIPT_ACTION_IGNORE
systemd.transfiletriggerin, RPMOSTREE_SCRIPT_ACTION_IGNORE /* Just does a daemon-reload which we don't want offline */
man-db.transfiletriggerin, RPMOSTREE_SCRIPT_ACTION_IGNORE /* https://bugzilla.redhat.com/show_bug.cgi?id=1473402 */

View File

@ -25,10 +25,13 @@
#include "rpmostree-output.h"
#include "rpmostree-bwrap.h"
#include <err.h>
#include <systemd/sd-journal.h>
#include "libglnx.h"
#include "rpmostree-scripts.h"
#define RPMOSTREE_MESSAGE_FILETRIGGER SD_ID128_MAKE(ef,dd,0e,4e,79,ca,45,d3,88,76,ac,45,e1,28,23,68)
/* This bit is currently private in librpm */
enum rpmscriptFlags_e {
RPMSCRIPT_FLAG_NONE = 0,
@ -90,11 +93,25 @@ static const KnownRpmScriptKind unsupported_scripts[] = {
RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG, RPMTAG_VERIFYSCRIPTFLAGS},
};
static gboolean
fail_if_interp_is_lua (const char *pkg_name,
const char *interp,
const char *script_desc,
GError **error)
{
static const char lua_builtin[] = "<lua>";
if (g_strcmp0 (interp, lua_builtin) == 0)
return glnx_throw (error, "Package '%s' has (currently) unsupported %s script in '%s'",
pkg_name, lua_builtin, script_desc);
return TRUE;
}
static RpmOstreeScriptAction
lookup_script_action (DnfPackage *package,
lookup_script_action (const char *pkg_name,
const char *scriptdesc)
{
const char *pkg_script = glnx_strjoina (dnf_package_get_name (package), ".", scriptdesc+1);
const char *pkg_script = glnx_strjoina (pkg_name, ".", scriptdesc+1);
const struct RpmOstreePackageScriptHandler *handler = rpmostree_script_gperf_lookup (pkg_script, strlen (pkg_script));
if (!handler)
return RPMOSTREE_SCRIPT_ACTION_DEFAULT;
@ -116,7 +133,7 @@ rpmostree_script_txn_validate (DnfPackage *package,
if (!(headerIsEntry (hdr, tagval) || headerIsEntry (hdr, progtagval)))
continue;
RpmOstreeScriptAction action = lookup_script_action (package, desc);
RpmOstreeScriptAction action = lookup_script_action (dnf_package_get_name (package), desc);
switch (action)
{
case RPMOSTREE_SCRIPT_ACTION_DEFAULT:
@ -130,6 +147,15 @@ rpmostree_script_txn_validate (DnfPackage *package,
return TRUE;
}
static void
script_child_setup_stdin (gpointer data)
{
int fd = GPOINTER_TO_INT (data);
if (dup2 (fd, 0) < 0)
err (1, "dup2");
}
/* Lowest level script handler in this file; create a bwrap instance and run it
* synchronously.
*/
@ -140,6 +166,7 @@ run_script_in_bwrap_container (int rootfs_fd,
const char *interp,
const char *script,
const char *script_arg,
int stdin_fd,
GCancellable *cancellable,
GError **error)
{
@ -203,6 +230,9 @@ run_script_in_bwrap_container (int rootfs_fd,
if (!bwrap)
goto out;
if (stdin_fd >= 0)
rpmostree_bwrap_set_child_setup (bwrap, script_child_setup_stdin, GINT_TO_POINTER (stdin_fd));
rpmostree_bwrap_append_child_argv (bwrap,
interp,
postscript_path_container,
@ -246,11 +276,8 @@ impl_run_rpm_script (const KnownRpmScriptKind *rpmscript,
const char *interp = (args && args[0]) ? args[0] : "/bin/sh";
/* Check for lua; see also https://github.com/projectatomic/rpm-ostree/issues/749 */
static const char lua_builtin[] = "<lua>";
if (g_strcmp0 (interp, lua_builtin) == 0)
return glnx_throw (error, "Package '%s' has (currently) unsupported %s script in '%s'",
dnf_package_get_name (pkg), lua_builtin, rpmscript->desc);
if (!fail_if_interp_is_lua (interp, dnf_package_get_name (pkg), rpmscript->desc, error))
return FALSE;
/* http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html#S2-RPM-INSIDE-ERASE-TIME-SCRIPTS */
const char *script_arg = NULL;
switch (dnf_package_get_action (pkg))
@ -278,7 +305,7 @@ impl_run_rpm_script (const KnownRpmScriptKind *rpmscript,
if (!run_script_in_bwrap_container (rootfs_fd, dnf_package_get_name (pkg),
rpmscript->desc, interp, script, script_arg,
cancellable, error))
-1, cancellable, error))
return glnx_prefix_error (error, "Running %s for %s", rpmscript->desc, dnf_package_get_name (pkg));
return TRUE;
}
@ -305,7 +332,7 @@ run_script (const KnownRpmScriptKind *rpmscript,
return TRUE;
const char *desc = rpmscript->desc;
RpmOstreeScriptAction action = lookup_script_action (pkg, desc);
RpmOstreeScriptAction action = lookup_script_action (dnf_package_get_name (pkg), desc);
switch (action)
{
case RPMOSTREE_SCRIPT_ACTION_IGNORE:
@ -318,6 +345,115 @@ run_script (const KnownRpmScriptKind *rpmscript,
cancellable, error);
}
#ifdef BUILDOPT_HAVE_RPM_FILETRIGGERS
static gboolean
write_filename (FILE *f, GString *prefix,
GError **error)
{
if (fwrite_unlocked (prefix->str, 1, prefix->len, f) != prefix->len)
return glnx_throw_errno_prefix (error, "fwrite");
if (fputc_unlocked ('\n', f) == EOF)
return glnx_throw_errno_prefix (error, "fputc");
return TRUE;
}
/* Used for %transfiletriggerin - basically an implementation of `find -type f` that
* writes the filenames to a tmpfile.
*/
static gboolean
write_subdir (int dfd, const char *path,
GString *prefix,
FILE *f,
guint *inout_n_matched,
GCancellable *cancellable,
GError **error)
{
/* This is an inlined copy of ot_dfd_iter_init_allow_noent()...if more users
* appear we should probably push to libglnx.
*/
g_assert (*path != '/');
glnx_fd_close int target_dfd = glnx_opendirat_with_errno (dfd, path, TRUE);
if (target_dfd < 0)
{
if (errno != ENOENT)
return glnx_throw_errno_prefix (error, "opendirat");
/* Not early return */
return TRUE;
}
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
if (!glnx_dirfd_iterator_init_take_fd (&target_dfd, &dfd_iter, error))
return FALSE;
while (TRUE)
{
struct dirent *dent;
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
return FALSE;
if (dent == NULL)
break;
const size_t origlen = prefix->len;
g_string_append_c (prefix, '/');
g_string_append (prefix, dent->d_name);
if (dent->d_type == DT_DIR)
{
if (!write_subdir (dfd_iter.fd, dent->d_name, prefix, f,
inout_n_matched,
cancellable, error))
return FALSE;
}
else
{
if (!write_filename (f, prefix, error))
return FALSE;
(*inout_n_matched)++;
}
g_string_truncate (prefix, origlen);
}
return TRUE;
}
/* Given file trigger @pattern (really a subdirectory), traverse the
* filesystem @rootfs_fd and write all matches as file names to @f. Used
* for %transfiletriggerin.
*/
static gboolean
find_and_write_matching_files (int rootfs_fd, const char *pattern,
FILE *f,
guint *out_n_matches,
GCancellable *cancellable,
GError **error)
{
GLNX_AUTO_PREFIX_ERROR ("Finding matches", error);
/* Fontconfig in fedora has /usr/local; we don't support RPM
* touching /usr/local. While I'm here, proactively
* require /usr as a prefix too.
*/
if (g_str_has_prefix (pattern, "usr/local") ||
!g_str_has_prefix (pattern, "usr/"))
return TRUE;
/* The printed buffer does have a leading / */
g_autoptr(GString) buf = g_string_new ("/");
g_string_append (buf, pattern);
/* Strip trailing '/' in the mutable copy we have here */
while (buf->len > 0 && buf->str[buf->len-1] == '/')
g_string_truncate (buf, buf->len - 1);
guint n_pattern_matches = 0;
if (!write_subdir (rootfs_fd, pattern, buf, f, &n_pattern_matches,
cancellable, error))
return glnx_prefix_error (error, "pattern '%s'", pattern);
*out_n_matches += n_pattern_matches;
return TRUE;
}
#endif
/* Execute all post/post-transaction scripts for @pkg */
gboolean
rpmostree_posttrans_run_sync (DnfPackage *pkg,
Header hdr,
@ -325,16 +461,192 @@ rpmostree_posttrans_run_sync (DnfPackage *pkg,
GCancellable *cancellable,
GError **error)
{
/* We treat %post and %posttrans equivalently, so do those in one go */
for (guint i = 0; i < G_N_ELEMENTS (posttrans_scripts); i++)
{
if (!run_script (&posttrans_scripts[i], pkg, hdr, rootfs_fd,
cancellable, error))
return FALSE;
}
return TRUE;
}
/* File triggers, as used by e.g. glib2.spec and vagrant.spec in Fedora. More
* info at <http://rpm.org/user_doc/file_triggers.html>.
*/
gboolean
rpmostree_transfiletriggers_run_sync (Header hdr,
int rootfs_fd,
GCancellable *cancellable,
GError **error)
{
#ifdef BUILDOPT_HAVE_RPM_FILETRIGGERS
const char *pkg_name = headerGetString (hdr, RPMTAG_NAME);
g_assert (pkg_name);
g_autofree char *error_prefix = g_strconcat ("Executing %transfiletriggerin for ", pkg_name, NULL);
GLNX_AUTO_PREFIX_ERROR (error_prefix, error);
RpmOstreeScriptAction action = lookup_script_action (pkg_name, "%transfiletriggerin");
switch (action)
{
case RPMOSTREE_SCRIPT_ACTION_IGNORE:
return TRUE; /* Note early return */
case RPMOSTREE_SCRIPT_ACTION_DEFAULT:
break; /* Continue below */
}
headerGetFlags hgflags = HEADERGET_MINMEM;
struct rpmtd_s tname, tscripts, tprogs, tflags, tscriptflags;
struct rpmtd_s tindex;
headerGet (hdr, RPMTAG_TRANSFILETRIGGERNAME, &tname, hgflags);
headerGet (hdr, RPMTAG_TRANSFILETRIGGERSCRIPTS, &tscripts, hgflags);
headerGet (hdr, RPMTAG_TRANSFILETRIGGERSCRIPTPROG, &tprogs, hgflags);
headerGet (hdr, RPMTAG_TRANSFILETRIGGERFLAGS, &tflags, hgflags);
headerGet (hdr, RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS, &tscriptflags, hgflags);
headerGet (hdr, RPMTAG_TRANSFILETRIGGERINDEX, &tindex, hgflags);
g_debug ("pkg %s transtrigger count %u/%u/%u/%u/%u/%u\n",
pkg_name, rpmtdCount (&tname), rpmtdCount (&tscripts),
rpmtdCount (&tprogs), rpmtdCount (&tflags), rpmtdCount (&tscriptflags),
rpmtdCount (&tindex));
if (rpmtdCount (&tscripts) == 0)
return TRUE;
const guint n_scripts = rpmtdCount (&tscripts);
const guint n_names = rpmtdCount (&tname);
/* Given multiple matching patterns, RPM expands it into multiple copies.
* The trigger index (AIUI) defines where to find the pattern (and flags) given a
* script.
*
* Some librpm source references:
* - tagexts.c:triggercondsTagFor()
* - rpmscript.c:rpmScriptFromTriggerTag()
*/
for (guint i = 0; i < n_scripts; i++)
{
rpmtdInit (&tname);
rpmtdInit (&tflags);
rpmFlags flags = 0;
if (rpmtdSetIndex (&tscriptflags, i) >= 0)
flags = rpmtdGetNumber (&tscriptflags);
g_assert_cmpint (rpmtdSetIndex (&tprogs, i), ==, i);
const char *interp = rpmtdGetString (&tprogs);
if (!interp)
interp = "/bin/sh";
if (!fail_if_interp_is_lua (interp, pkg_name, "%transfiletriggerin", error))
return FALSE;
g_autofree char *script_owned = NULL;
g_assert_cmpint (rpmtdSetIndex (&tscripts, i), ==, i);
const char *script = rpmtdGetString (&tscripts);
if (!script)
continue;
if (flags & RPMSCRIPT_FLAG_EXPAND)
script = script_owned = rpmExpand (script, NULL);
g_autoptr(GPtrArray) patterns = g_ptr_array_new_with_free_func (g_free);
/* Iterate over the trigger "names" which are file patterns */
for (guint j = 0; j < n_names; j++)
{
g_assert_cmpint (rpmtdSetIndex (&tindex, j), ==, j);
guint32 tindex_num = *rpmtdGetUint32 (&tindex);
if (tindex_num != i)
continue;
rpmFlags sense = 0;
if (rpmtdSetIndex (&tflags, j) >= 0)
sense = rpmtdGetNumber (&tflags);
/* See if this is a triggerin (as opposed to triggerun, which we)
* don't execute.
*/
const gboolean trigger_in = (sense & RPMSENSE_TRIGGERIN) > 0;
if (!trigger_in)
continue;
g_assert_cmpint (rpmtdSetIndex (&tname, j), ==, j);
const char *pattern = rpmtdGetString (&tname);
if (!pattern)
continue;
/* Skip leading `/`, since we use fd-relative access */
pattern += strspn (pattern, "/");
/* Silently ignore broken patterns for now */
if (!*pattern)
continue;
g_ptr_array_add (patterns, g_strdup (pattern));
}
if (patterns->len == 0)
continue;
/* Build up the list of files matching the patterns. librpm uses a pipe and
* doesn't do async writes, and hence is subject to deadlock. We could use
* a pipe and do async, but an O_TMPFILE is easier for now. There
* shouldn't be megabytes of data here, and the parallelism loss in
* practice I think is going to be small.
*/
g_auto(GLnxTmpfile) matching_files_tmpf = { 0, };
if (!glnx_open_anonymous_tmpfile (O_RDWR | O_CLOEXEC, &matching_files_tmpf, error))
return FALSE;
g_autoptr(FILE) tmpf_file = fdopen (matching_files_tmpf.fd, "w");
if (!tmpf_file)
return glnx_throw_errno_prefix (error, "fdopen");
g_autoptr(GString) patterns_joined = g_string_new ("");
guint n_total_matched = 0;
for (guint j = 0; j < patterns->len; j++)
{
guint n_matched = 0;
const char *pattern = patterns->pdata[j];
if (j > 0)
g_string_append (patterns_joined, ", ");
g_string_append (patterns_joined, pattern);
if (!find_and_write_matching_files (rootfs_fd, pattern, tmpf_file, &n_matched,
cancellable, error))
return FALSE;
if (n_matched == 0)
{
/* This is probably a bug...let's log it */
sd_journal_print (LOG_INFO, "No files matched %%transfiletriggerin(%s) for %s", pattern, pkg_name);
}
n_total_matched += n_matched;
}
if (n_total_matched == 0)
continue;
if (!glnx_stdio_file_flush (tmpf_file, error))
return FALSE;
/* Now, point back to the beginning so the script reads it from the start
as stdin */
if (lseek (matching_files_tmpf.fd, 0, SEEK_SET) < 0)
return glnx_throw_errno_prefix (error, "lseek");
/* Run it, and log the result */
if (!run_script_in_bwrap_container (rootfs_fd, pkg_name,
"%transfiletriggerin", interp, script, NULL,
matching_files_tmpf.fd, cancellable, error))
return FALSE;
sd_journal_send ("MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(RPMOSTREE_MESSAGE_FILETRIGGER),
"MESSAGE=Executed %%transfiletriggerin(%s) for %s; %u matched files",
pkg_name, patterns_joined->str, n_total_matched,
"SCRIPT_TYPE=%%transfiletriggerin"
"PKG=%s", pkg_name,
"PATTERNS=%s", patterns_joined->str,
"TRIGGER_N_MATCHES=%u", n_total_matched,
NULL);
}
#endif
return TRUE;
}
/* Execute all pre-install scripts for @pkg */
gboolean
rpmostree_pre_run_sync (DnfPackage *pkg,
Header hdr,

View File

@ -58,6 +58,12 @@ rpmostree_posttrans_run_sync (DnfPackage *pkg,
GCancellable *cancellable,
GError **error);
gboolean
rpmostree_transfiletriggers_run_sync (Header hdr,
int rootfs_fd,
GCancellable *cancellable,
GError **error);
gboolean
rpmostree_pre_run_sync (DnfPackage *pkg,
Header hdr,

View File

@ -378,6 +378,9 @@ License: GPLv2+
EOF
local build= install= files= pretrans= pre= post= posttrans= post_args=
local transfiletriggerin= transfiletriggerin_patterns=
local transfiletriggerin2= transfiletriggerin2_patterns=
local transfiletriggerun= transfiletriggerun_patterns=
while [ $# -ne 0 ]; do
local section=$1; shift
local arg=$1; shift
@ -392,6 +395,15 @@ EOF
post_args="$arg";;
version|release|arch|build|install|files|pretrans|pre|post|posttrans)
declare $section="$arg";;
transfiletriggerin)
transfiletriggerin_patterns="$arg";
declare $section="$1"; shift;;
transfiletriggerin2)
transfiletriggerin2_patterns="$arg";
declare $section="$1"; shift;;
transfiletriggerun)
transfiletriggerun_patterns="$arg";
declare $section="$1"; shift;;
*)
assert_not_reached "unhandled section $section";;
esac
@ -423,6 +435,15 @@ $post
${posttrans:+%posttrans}
$posttrans
${transfiletriggerin:+%transfiletriggerin -- ${transfiletriggerin_patterns}}
$transfiletriggerin
${transfiletriggerin2:+%transfiletriggerin -- ${transfiletriggerin2_patterns}}
$transfiletriggerin2
${transfiletriggerun:+%transfiletriggerun -- ${transfiletriggerun_patterns}}
$transfiletriggerun
%install
mkdir -p %{buildroot}/usr/bin
install $name %{buildroot}/usr/bin

View File

@ -77,6 +77,39 @@ vm_cmd cat /usr/lib/prefixtest.txt > prefixtest.txt
assert_file_has_content prefixtest.txt "/usr"
echo "ok script expansion"
vm_rpmostree rollback
vm_reboot
vm_rpmostree cleanup -p
# File triggers are Fedora+
if ! vm_cmd grep -q 'ID=.*centos' /etc/os-release; then
# We use /usr/share/licenses since it's small predictable content
license_combos="zlib-rpm systemd-tar-rpm sed-tzdata"
license_un_combos="zlib systemd-rpm"
vm_build_rpm scriptpkg4 \
transfiletriggerin "/usr/share/licenses/zlib /usr/share/licenses/rpm" 'sort >/usr/share/transfiletriggerin-license-zlib-rpm.txt' \
transfiletriggerun "/usr/share/licenses/zlib" 'sort >/usr/share/transfiletriggerun-license-zlib.txt'
vm_build_rpm scriptpkg5 \
transfiletriggerin "/usr/share/licenses/systemd /usr/share/licenses/rpm /usr/share/licenses/tar" 'sort >/usr/share/transfiletriggerin-license-systemd-tar-rpm.txt' \
transfiletriggerun "/usr/share/licenses/systemd /usr/share/licenses/rpm" 'sort >/usr/share/transfiletriggerun-license-systemd-rpm.txt' \
transfiletriggerin2 "/usr/share/licenses/sed /usr/share/licenses/tzdata" 'sort >/usr/share/transfiletriggerin-license-sed-tzdata.txt'
vm_rpmostree pkg-add scriptpkg{4,5}
vm_rpmostree ex livefs
for combo in ${license_combos}; do
vm_cmd cat /usr/share/transfiletriggerin-license-${combo}.txt > transfiletriggerin-license-${combo}.txt
rm -f transfiletriggerin-fs-${combo}.txt.tmp
(for path in $(echo ${combo} | sed -e 's,-, ,g'); do
vm_cmd find /usr/share/licenses/${path} -type f
done) | sort > transfiletriggerin-fs-license-${combo}.txt
diff -u transfiletriggerin-license-${combo}.txt transfiletriggerin-fs-license-${combo}.txt
done
for combo in ${license_un_combos}; do
vm_cmd test '!' -f /usr/share/licenses/transfiletriggerun-license-${combo}.txt
done
# We really need a reset command to go back to the base layer
vm_rpmostree uninstall scriptpkg{4,5}
echo "ok transfiletriggerin"
fi
# And now, things that should fail
vm_build_rpm rofiles-violation \
post "echo should fail >> /usr/share/licenses/glibc/COPYING"
@ -84,8 +117,6 @@ if vm_rpmostree install rofiles-violation; then
assert_not_reached "installed test-post-rofiles-violation!"
fi
# We really need a reset command to go back to the base layer
vm_rpmostree uninstall scriptpkg{1,2,3}
vm_cmd 'useradd testuser || true'
vm_cmd touch /home/testuser/somedata /tmp/sometmpfile /var/tmp/sometmpfile
vm_build_rpm rmrf post "rm --no-preserve-root -rf / &>/dev/null || true"