diff --git a/configure.ac b/configure.ac index 9503cc63..9503ae7b 100644 --- a/configure.ac +++ b/configure.ac @@ -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 " diff --git a/src/libpriv/rpmostree-core.c b/src/libpriv/rpmostree-core.c index d9ff2835..68e25d54 100644 --- a/src/libpriv/rpmostree-core.c +++ b/src/libpriv/rpmostree-core.c @@ -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 && diff --git a/src/libpriv/rpmostree-script-gperf.gperf b/src/libpriv/rpmostree-script-gperf.gperf index f3276c2d..898351b7 100644 --- a/src/libpriv/rpmostree-script-gperf.gperf +++ b/src/libpriv/rpmostree-script-gperf.gperf @@ -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 */ diff --git a/src/libpriv/rpmostree-scripts.c b/src/libpriv/rpmostree-scripts.c index 44829b09..f79ee67b 100644 --- a/src/libpriv/rpmostree-scripts.c +++ b/src/libpriv/rpmostree-scripts.c @@ -25,10 +25,13 @@ #include "rpmostree-output.h" #include "rpmostree-bwrap.h" #include +#include #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[] = ""; + 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[] = ""; - 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 . + */ +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, diff --git a/src/libpriv/rpmostree-scripts.h b/src/libpriv/rpmostree-scripts.h index e6e6bdc9..c523c72c 100644 --- a/src/libpriv/rpmostree-scripts.h +++ b/src/libpriv/rpmostree-scripts.h @@ -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, diff --git a/tests/common/libtest.sh b/tests/common/libtest.sh index 874dc166..262d883c 100644 --- a/tests/common/libtest.sh +++ b/tests/common/libtest.sh @@ -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 diff --git a/tests/vmcheck/test-layering-scripts.sh b/tests/vmcheck/test-layering-scripts.sh index c6497acf..f2b22607 100755 --- a/tests/vmcheck/test-layering-scripts.sh +++ b/tests/vmcheck/test-layering-scripts.sh @@ -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"