Determine when to perform Collection actions
authorSteve Lawrence <slawrence@tresys.com>
Mon, 21 Jun 2010 21:04:37 +0000 (17:04 -0400)
committerPanu Matilainen <pmatilai@redhat.com>
Tue, 22 Jun 2010 08:12:43 +0000 (11:12 +0300)
There are three times during a transaction when Collection actions can be
performed:

1) After the last time a member of a collection is either installed or removed
2) After the last time a member of a collection is installed only
3) Before the first time a member of a collection is removed only

This patch adds three lists to the rpmte structure to mark which transaction
elements fall into each of these groups, and the collections that caused that.
A new function is added to the TSM to scan through all the transaction elements
and update these lists. When a collection is added to one of these lists, it
signifies that when that transaction element is installed, the appropriate
action should be performed for that collection.

lib/rpmte.c
lib/rpmte.h
lib/rpmte_internal.h
lib/transaction.c

index e622558..2c33605 100644 (file)
@@ -63,6 +63,11 @@ struct rpmte_s {
     int failed;                        /*!< (parent) install/erase failed */
 
     rpmfs fs;
+
+    ARGV_t lastInCollectionsAny;       /*!< list of collections this te is the last to be installed or removed */
+    ARGV_t lastInCollectionsAdd;       /*!< list of collections this te is the last to be only installed */
+    ARGV_t firstInCollectionsRemove;   /*!< list of collections this te is the first to be only removed */
+    ARGV_t collections;                        /*!< list of collections */
 };
 
 /* forward declarations */
@@ -185,6 +190,8 @@ static void buildRelocs(rpmte p, Header h, rpmRelocation *relocs)
  */
 static void addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs)
 {
+    struct rpmtd_s colls;
+
     p->name = headerGetAsString(h, RPMTAG_NAME);
     p->version = headerGetAsString(h, RPMTAG_VERSION);
     p->release = headerGetAsString(h, RPMTAG_RELEASE);
@@ -228,6 +235,19 @@ static void addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs)
                         headerIsEntry(h, RPMTAG_POSTTRANSPROG)) ?
                        RPMTE_HAVE_POSTTRANS : 0;
 
+    p->lastInCollectionsAny = NULL;
+    p->lastInCollectionsAdd = NULL;
+    p->firstInCollectionsRemove = NULL;
+    p->collections = NULL;
+    if (headerGet(h, RPMTAG_COLLECTIONS, &colls, HEADERGET_MINMEM)) {
+       const char *collname;
+       while ((collname = rpmtdNextString(&colls))) {
+           argvAdd(&p->collections, collname);
+       }
+       argvSort(p->collections, NULL);
+       rpmtdFreeData(&colls);
+    }
+
     rpmteColorDS(p, RPMTAG_PROVIDENAME);
     rpmteColorDS(p, RPMTAG_REQUIRENAME);
     return;
@@ -261,6 +281,11 @@ rpmte rpmteFree(rpmte te)
        rpmteCleanDS(te);
        rpmtsUnlink(te->ts);
 
+       argvFree(te->collections);
+       argvFree(te->lastInCollectionsAny);
+       argvFree(te->lastInCollectionsAdd);
+       argvFree(te->firstInCollectionsRemove);
+
        memset(te, 0, sizeof(*te));     /* XXX trash and burn */
        free(te);
     }
@@ -368,6 +393,46 @@ rpm_color_t rpmteSetColor(rpmte te, rpm_color_t color)
     return ocolor;
 }
 
+ARGV_const_t rpmteCollections(rpmte te)
+{
+    return (te != NULL) ? te->collections : NULL;
+}
+
+int rpmteHasCollection(rpmte te, const char *collname)
+{
+    return (argvSearch(rpmteCollections(te), collname, NULL) != NULL);
+}
+
+int rpmteAddToLastInCollectionAdd(rpmte te, const char *collname)
+{
+    if (te != NULL) {
+       argvAdd(&te->lastInCollectionsAdd, collname);
+       argvSort(te->lastInCollectionsAdd, NULL);
+       return 0;
+    }
+    return -1;
+}
+
+int rpmteAddToLastInCollectionAny(rpmte te, const char *collname)
+{
+    if (te != NULL) {
+       argvAdd(&te->lastInCollectionsAny, collname);
+       argvSort(te->lastInCollectionsAny, NULL);
+       return 0;
+    }
+    return -1;
+}
+
+int rpmteAddToFirstInCollectionRemove(rpmte te, const char *collname)
+{
+    if (te != NULL) {
+       argvAdd(&te->firstInCollectionsRemove, collname);
+       argvSort(te->firstInCollectionsRemove, NULL);
+       return 0;
+    }
+    return -1;
+}
+
 rpm_loff_t rpmtePkgFileSize(rpmte te)
 {
     return (te != NULL ? te->pkgFileSize : 0);
index 0d1fc8d..17854d7 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <rpm/rpmtypes.h>
+#include <rpm/argv.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -236,6 +237,22 @@ rpmds rpmteDS(rpmte te, rpmTag tag);
  */
 rpmfi rpmteFI(rpmte te);
 
+/** \ingroup rpmte
+ * Retrieve list of collections
+ * @param te           transaction element
+ * @return             list of collections
+ */
+ARGV_const_t rpmteCollections(rpmte te);
+
+/** \ingroup rpmte
+ * Determine a transaction element is part of a collection
+ * @param te           transaction element
+ * @param collname     collection name
+ * @return             1 if collname is part of a collection, 0 if not
+ */
+int rpmteHasCollection(rpmte te, const char * collname);
+
+
 #ifdef __cplusplus
 }
 #endif
index 0aa78b2..2425868 100644 (file)
@@ -104,5 +104,36 @@ unsigned int rpmteHeaderSize(rpmte te);
 RPM_GNUC_INTERNAL
 rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal);
 
+/** \ingroup rpmte
+ * Add a collection to the list of last collections for the installation
+ * section of a transaction element
+ * @param te           transaction element
+ * @param collname             collection name
+ * @return             0 on success, non-zero on error
+ */
+RPM_GNUC_INTERNAL
+int rpmteAddToLastInCollectionAdd(rpmte te, const char * collname);
+
+/** \ingroup rpmte
+ * Add a collection to the list of last collections for the installation
+ * or removal section of a transaction element
+ * @param te           transaction element
+ * @param collname             collection name
+ * @return             0 on success, non-zero on error
+ */
+RPM_GNUC_INTERNAL
+int rpmteAddToLastInCollectionAny(rpmte te, const char * collname);
+
+/** \ingroup rpmte
+ * Add a collection to the list of first collections for the removal
+ * section of a transaction element
+ * @param te           transaction element
+ * @param collname             collection name
+ * @return             0 on success, non-zero on error
+ */
+RPM_GNUC_INTERNAL
+int rpmteAddToFirstInCollectionRemove(rpmte te, const char * collname);
+
+
 #endif /* _RPMTE_INTERNAL_H */
 
index 9881426..5202ee2 100644 (file)
@@ -1108,6 +1108,85 @@ static int runTransScripts(rpmts ts, pkgGoal goal)
     return 0; /* what to do about failures? */
 }
 
+static int rpmtsDetermineCollectionPoints(rpmts ts)
+{
+    /* seenCollectionsPost and TEs are basically a key-value pair. each item in
+     * seenCollectionsPost is a collection that has been seen from any package,
+     * and the associated index in the TEs is the last transaction element
+     * where that collection was seen. */
+    ARGV_t seenCollectionsPost = NULL;
+    rpmte *TEs = NULL;
+    int numSeenPost = 0;
+
+    /* seenCollectionsPre is a list of collections that have been seen from
+     * only removed packages */
+    ARGV_t seenCollectionsPre = NULL;
+    int numSeenPre = 0;
+
+    ARGV_const_t collname;
+    int installing = 1;
+    int i;
+
+    rpmte p;
+    rpmtsi pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
+       /* detect when we switch from installing to removing packages, and
+        * update the lastInCollectionAdd lists */
+       if (installing && rpmteType(p) == TR_REMOVED) {
+           installing = 0;
+           for (i = 0; i < numSeenPost; i++) {
+               rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]);
+           }
+       }
+
+       for (collname = rpmteCollections(p); collname && *collname; collname++) {
+           /* figure out if we've seen this collection in post before */
+           for (i = 0; i < numSeenPost && strcmp(*collname, seenCollectionsPost[i]); i++) {
+           }
+           if (i < numSeenPost) {
+               /* we've seen the collection, update the index */
+               TEs[i] = p;
+           } else {
+               /* haven't seen the collection yet, add it */
+               argvAdd(&seenCollectionsPost, *collname);
+               TEs = xrealloc(TEs, sizeof(*TEs) * (numSeenPost + 1));
+               TEs[numSeenPost] = p;
+               numSeenPost++;
+           }
+
+           /* figure out if we've seen this collection in pre remove before */
+           if (installing == 0) {
+               for (i = 0; i < numSeenPre && strcmp(*collname, seenCollectionsPre[i]); i++) {
+               }
+               if (i >= numSeenPre) {
+                   /* haven't seen this collection, add it */
+                   rpmteAddToFirstInCollectionRemove(p, *collname);
+                   argvAdd(&seenCollectionsPre, *collname);
+                   numSeenPre++;
+               }
+           }
+       }
+    }
+    pi = rpmtsiFree(pi);
+
+    /* we've looked at all the rpmte's, update the lastInCollectionAny lists */
+    for (i = 0; i < numSeenPost; i++) {
+       rpmteAddToLastInCollectionAny(TEs[i], seenCollectionsPost[i]);
+       if (installing == 1) {
+           /* lastInCollectionAdd is only updated above if packages were
+            * removed. if nothing is removed in the transaction, we need to
+            * update that list here */
+           rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]);
+       }
+    }
+
+    argvFree(seenCollectionsPost);
+    argvFree(seenCollectionsPre);
+    _free(TEs);
+
+    return 0;
+}
+
 /* Add fingerprint for each file not skipped. */
 static void addFingerprints(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPrintCache fpc)
 {
@@ -1347,6 +1426,8 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
        goto exit;
     }
 
+    rpmtsDetermineCollectionPoints(ts);
+
     /* Check package set for problems */
     tsprobs = checkProblems(ts);