7 #include <rpm/rpmtag.h>
8 #include <rpm/rpmmacro.h>
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmds.h>
12 #include "lib/rpmte_internal.h" /* XXX tsortInfo_s */
13 #include "lib/rpmts_internal.h"
18 * Strongly Connected Components
19 * set of packages (indirectly) requiering each other
22 int count; /* # of external requires this SCC has */
23 /* int qcnt; # of external requires pointing to this SCC */
24 int size; /* # of members */
28 typedef struct scc_s * scc;
31 tsortInfo rel_suc; // pkg requiring this package
32 rpmsenseFlags rel_flags; // accumulated flags of the requirements
33 struct relation_s * rel_next;
36 typedef struct relation_s * relation;
40 int tsi_count; // #pkgs this pkg requires
41 int tsi_qcnt; // #pkgs requiring this package
42 int tsi_reqx; // requires Idx/mark as (queued/loop)
43 struct relation_s * tsi_relations;
44 struct relation_s * tsi_forward_relations;
45 tsortInfo tsi_suc; // used for queuing (addQ)
46 int tsi_SccIdx; // # of the SCC the node belongs to
47 // (1 for trivial SCCs)
48 int tsi_SccLowlink; // used for SCC detection
51 static void rpmTSIFree(tsortInfo tsi)
55 while (tsi->tsi_relations != NULL) {
56 rel = tsi->tsi_relations;
57 tsi->tsi_relations = tsi->tsi_relations->rel_next;
60 while (tsi->tsi_forward_relations != NULL) {
61 rel = tsi->tsi_forward_relations;
62 tsi->tsi_forward_relations = \
63 tsi->tsi_forward_relations->rel_next;
68 static inline int addSingleRelation(rpmte p,
70 rpmsenseFlags dsflags)
72 struct tsortInfo_s *tsi_p, *tsi_q;
74 rpmElementType teType = rpmteType(p);
77 /* Avoid deps outside this transaction and self dependencies */
78 if (q == NULL || q == p)
81 /* Erasures are reversed installs. */
82 if (teType == TR_REMOVED) {
86 flags = isErasePreReq(dsflags);
88 flags = isInstallPreReq(dsflags);
91 /* map legacy prereq to pre/preun as needed */
92 if (isLegacyPreReq(dsflags)) {
93 flags |= (teType == TR_ADDED) ?
94 RPMSENSE_SCRIPT_PRE : RPMSENSE_SCRIPT_PREUN;
100 /* if relation got already added just update the flags */
101 if (tsi_q->tsi_relations && tsi_q->tsi_relations->rel_suc == tsi_p) {
102 tsi_q->tsi_relations->rel_flags |= flags;
103 tsi_p->tsi_forward_relations->rel_flags |= flags;
107 /* Record next "q <- p" relation (i.e. "p" requires "q"). */
109 /* bump p predecessor count */
113 rel = xcalloc(1, sizeof(*rel));
114 rel->rel_suc = tsi_p;
115 rel->rel_flags = flags;
117 rel->rel_next = tsi_q->tsi_relations;
118 tsi_q->tsi_relations = rel;
120 /* bump q successor count */
124 rel = xcalloc(1, sizeof(*rel));
125 rel->rel_suc = tsi_q;
126 rel->rel_flags = flags;
128 rel->rel_next = tsi_p->tsi_forward_relations;
129 tsi_p->tsi_forward_relations = rel;
135 * Record next "q <- p" relation (i.e. "p" requires "q").
136 * @param ts transaction set
137 * @param p predecessor (i.e. package that "Requires: q")
138 * @param requires relation
141 static inline int addRelation(rpmts ts,
147 rpmsenseFlags dsflags;
149 dsflags = rpmdsFlags(requires);
151 /* Avoid dependendencies which are not relevant for ordering */
152 if (dsflags & (RPMSENSE_RPMLIB|RPMSENSE_CONFIG|RPMSENSE_PRETRANS|RPMSENSE_POSTTRANS))
155 q = rpmalSatisfiesDepend(al, requires);
157 /* Avoid deps outside this transaction and self dependencies */
158 if (q == NULL || q == p)
161 addSingleRelation(p, q, dsflags);
167 * Collections might have special ordering requirements. Notably
168 * sepolicy collection requires having all the bits in the collection
169 * close to each other. We try to ensure this by creating a strongly
170 * connected component of such "grouped" collections, by introducing
171 * an artificial relation loop across the all its members.
173 static int addCollRelations(rpmal al, rpmte p, ARGV_t *seenColls)
177 for (qcolls = rpmteCollections(p); qcolls && *qcolls; qcolls++) {
179 if (argvSearch(*seenColls, *qcolls, NULL))
182 flags = rstrscat(NULL, "%{__collection_", *qcolls, "_flags}", NULL);
183 if (rpmExpandNumeric(flags) & 0x1) {
184 rpmte *tes = rpmalAllInCollection(al, *qcolls);
185 for (rpmte *te = tes; te && *te; te++) {
186 rpmte next = (*(te + 1) != NULL) ? *(te + 1) : *tes;
187 addSingleRelation(*te, next, RPMSENSE_ANY);
193 argvAdd(seenColls, *qcolls);
194 argvSort(*seenColls, NULL);
201 * Add element to list sorting by tsi_qcnt.
202 * @param p new element
203 * @retval qp address of first element
204 * @retval rp address of last element
207 static void addQ(tsortInfo p, tsortInfo * qp, tsortInfo * rp,
208 rpm_color_t prefcolor)
211 rpm_color_t pcolor = rpmteColor(p->te);
214 /* Mark the package as queued. */
217 if ((*rp) == NULL) { /* 1st element */
218 /* FIX: double indirection */
223 if (rpmteType(p->te) == TR_ADDED)
224 tailcond = (pcolor && pcolor != prefcolor);
226 tailcond = (pcolor && pcolor == prefcolor);
228 /* Find location in queue using metric tsi_qcnt and color. */
229 for (qprev = NULL, q = (*qp);
231 qprev = q, q = q->tsi_suc)
233 /* Place preferred color towards queue head on install, tail on erase */
234 if (tailcond && (pcolor != rpmteColor(q->te)))
237 if (q->tsi_qcnt <= p->tsi_qcnt)
241 if (qprev == NULL) { /* insert at beginning of list */
243 (*qp) = p; /* new head */
244 } else if (q == NULL) { /* insert at end of list */
246 (*rp) = p; /* new tail */
247 } else { /* insert between qprev and q */
253 typedef struct sccData_s {
254 int index; /* DFS node number counter */
255 tsortInfo *stack; /* Stack of nodes */
256 int stackcnt; /* Stack top counter */
257 scc SCCs; /* Array of SCC's found */
258 int sccCnt; /* Number of SCC's found */
261 static void tarjan(sccData sd, tsortInfo tsi)
266 /* use negative index numbers */
268 /* Set the depth index for p */
269 tsi->tsi_SccIdx = sd->index;
270 tsi->tsi_SccLowlink = sd->index;
272 sd->stack[sd->stackcnt++] = tsi; /* Push p on the stack */
273 for (rel=tsi->tsi_relations; rel != NULL; rel=rel->rel_next) {
274 /* Consider successors of p */
275 tsi_q = rel->rel_suc;
276 if (tsi_q->tsi_SccIdx > 0)
277 /* Ignore already found SCCs */
279 if (tsi_q->tsi_SccIdx == 0){
280 /* Was successor q not yet visited? */
281 tarjan(sd, tsi_q); /* Recurse */
282 /* negative index numers: use max as it is closer to 0 */
283 tsi->tsi_SccLowlink = (
284 tsi->tsi_SccLowlink > tsi_q->tsi_SccLowlink
285 ? tsi->tsi_SccLowlink : tsi_q->tsi_SccLowlink);
287 tsi->tsi_SccLowlink = (
288 tsi->tsi_SccLowlink > tsi_q->tsi_SccIdx
289 ? tsi->tsi_SccLowlink : tsi_q->tsi_SccIdx);
293 if (tsi->tsi_SccLowlink == tsi->tsi_SccIdx) {
294 /* v is the root of an SCC? */
295 if (sd->stack[sd->stackcnt-1] == tsi) {
296 /* ignore trivial SCCs */
297 tsi_q = sd->stack[--sd->stackcnt];
298 tsi_q->tsi_SccIdx = 1;
300 int stackIdx = sd->stackcnt;
302 tsi_q = sd->stack[--stackIdx];
303 tsi_q->tsi_SccIdx = sd->sccCnt;
304 } while (tsi_q != tsi);
306 stackIdx = sd->stackcnt;
308 tsi_q = sd->stack[--stackIdx];
309 /* Calculate count for the SCC */
310 sd->SCCs[sd->sccCnt].count += tsi_q->tsi_count;
311 /* Subtract internal relations */
312 for (rel=tsi_q->tsi_relations; rel != NULL;
314 if (rel->rel_suc != tsi_q &&
315 rel->rel_suc->tsi_SccIdx == sd->sccCnt)
316 sd->SCCs[sd->sccCnt].count--;
318 } while (tsi_q != tsi);
319 sd->SCCs[sd->sccCnt].size = sd->stackcnt - stackIdx;
321 sd->SCCs[sd->sccCnt].members = xcalloc(sd->SCCs[sd->sccCnt].size,
323 memcpy(sd->SCCs[sd->sccCnt].members, sd->stack + stackIdx,
324 sd->SCCs[sd->sccCnt].size * sizeof(tsortInfo));
325 sd->stackcnt = stackIdx;
331 /* Search for SCCs and return an array last entry has a .size of 0 */
332 static scc detectSCCs(tsortInfo orderInfo, int nelem, int debugloops)
334 /* Set up data structures needed for the tarjan algorithm */
335 scc SCCs = xcalloc(nelem+3, sizeof(*SCCs));
336 tsortInfo *stack = xcalloc(nelem, sizeof(*stack));
337 struct sccData_s sd = { 0, stack, 0, SCCs, 2 };
339 for (int i = 0; i < nelem; i++) {
340 tsortInfo tsi = &orderInfo[i];
341 /* Start a DFS at each node */
342 if (tsi->tsi_SccIdx == 0)
348 SCCs = xrealloc(SCCs, (sd.sccCnt+1)*sizeof(struct scc_s));
352 int msglvl = debugloops ? RPMLOG_WARNING : RPMLOG_DEBUG;
353 rpmlog(msglvl, "%i Strongly Connected Components\n", sd.sccCnt-2);
354 for (int i = 2; i < sd.sccCnt; i++) {
355 rpmlog(msglvl, "SCC #%i: %i members (%i external dependencies)\n",
356 i-1, SCCs[i].size, SCCs[i].count);
358 /* loop over members */
359 for (int j = 0; j < SCCs[i].size; j++) {
360 tsortInfo member = SCCs[i].members[j];
361 rpmlog(msglvl, "\t%s\n", rpmteNEVRA(member->te));
362 /* show relations between members */
363 relation rel = member->tsi_forward_relations;
364 for (; rel != NULL; rel=rel->rel_next) {
365 if (rel->rel_suc->tsi_SccIdx!=i) continue;
366 rpmlog(msglvl, "\t\t%s %s\n",
367 rel->rel_flags ? "=>" : "->",
368 rpmteNEVRA(rel->rel_suc->te));
376 static void collectTE(rpm_color_t prefcolor, tsortInfo q,
377 rpmte * newOrder, int * newOrderCount,
379 tsortInfo * queue_end,
380 tsortInfo * outer_queue,
381 tsortInfo * outer_queue_end)
383 char deptypechar = (rpmteType(q->te) == TR_REMOVED ? '-' : '+');
387 /* figure depth in tree for nice formatting */
388 for (rpmte p = q->te; (p = rpmteParent(p)); depth++) {}
389 rpmlog(RPMLOG_DEBUG, "%5d%5d%5d%5d %*s%c%s\n",
390 *newOrderCount, q->tsi_count, q->tsi_qcnt,
391 depth, (2 * depth), "",
392 deptypechar, rpmteNEVRA(q->te));
395 newOrder[*newOrderCount] = q->te;
398 /* T6. Erase relations. */
399 for (relation rel = q->tsi_relations; rel != NULL; rel = rel->rel_next) {
400 tsortInfo p = rel->rel_suc;
401 /* ignore already collected packages */
402 if (p->tsi_SccIdx == 0) continue;
403 if (p == q) continue;
405 if (p && (--p->tsi_count) == 0) {
406 (void) rpmteSetParent(p->te, q->te);
408 if (q->tsi_SccIdx > 1 && q->tsi_SccIdx != p->tsi_SccIdx) {
409 /* Relation point outside of this SCC: add to outside queue */
410 assert(outer_queue != NULL && outer_queue_end != NULL);
411 addQ(p, outer_queue, outer_queue_end, prefcolor);
413 addQ(p, &q->tsi_suc, queue_end, prefcolor);
416 if (p && p->tsi_SccIdx > 1 &&
417 p->tsi_SccIdx != q->tsi_SccIdx) {
418 if (--SCCs[p->tsi_SccIdx].count == 0) {
419 /* New SCC is ready, add this package as representative */
420 (void) rpmteSetParent(p->te, q->te);
422 if (outer_queue != NULL) {
423 addQ(p, outer_queue, outer_queue_end, prefcolor);
425 addQ(p, &q->tsi_suc, queue_end, prefcolor);
433 static void dijkstra(const struct scc_s *SCC, int sccNr)
438 /* can use a simple queue as edge weights are always 1 */
439 tsortInfo * queue = xmalloc((SCC->size+1) * sizeof(*queue));
442 * Find packages that are prerequired and use them as
443 * starting points for the Dijkstra algorithm
446 for (int i = 0; i < SCC->size; i++) {
447 tsortInfo tsi = SCC->members[i];
448 tsi->tsi_SccLowlink = INT_MAX;
449 for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
450 if (rel->rel_flags && rel->rel_suc->tsi_SccIdx == sccNr) {
451 if (rel->rel_suc != tsi) {
452 tsi->tsi_SccLowlink = 0;
455 tsi->tsi_SccLowlink = INT_MAX/2;
462 if (start == end) { /* no regular prereqs; add self prereqs to queue */
463 for (int i = 0; i < SCC->size; i++) {
464 tsortInfo tsi = SCC->members[i];
465 if (tsi->tsi_SccLowlink != INT_MAX) {
472 while (start != end) {
473 tsortInfo tsi = queue[start++];
474 for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
475 tsortInfo next_tsi = rel->rel_suc;
476 if (next_tsi->tsi_SccIdx != sccNr) continue;
477 if (next_tsi->tsi_SccLowlink > tsi->tsi_SccLowlink+1) {
478 next_tsi->tsi_SccLowlink = tsi->tsi_SccLowlink + 1;
479 queue[end++] = rel->rel_suc;
486 static void collectSCC(rpm_color_t prefcolor, tsortInfo p_tsi,
487 rpmte * newOrder, int * newOrderCount,
488 scc SCCs, tsortInfo * queue_end)
490 int sccNr = p_tsi->tsi_SccIdx;
491 const struct scc_s * SCC = SCCs+sccNr;
493 /* remove p from the outer queue */
494 tsortInfo outer_queue_start = p_tsi->tsi_suc;
495 p_tsi->tsi_suc = NULL;
498 * Run a multi source Dijkstra's algorithm to find relations
499 * that can be zapped with least danger to pre reqs.
500 * As weight of the edges is always 1 it is not necessary to
501 * sort the vertices by distance as the queue gets them
504 dijkstra(SCC, sccNr);
507 tsortInfo best = NULL;
508 tsortInfo inner_queue_start, inner_queue_end;
511 /* select best candidate to start with */
512 for (int i = 0; i < SCC->size; i++) {
513 tsortInfo tsi = SCC->members[i];
514 if (tsi->tsi_SccIdx == 0) /* package already collected */
516 if (tsi->tsi_SccLowlink >= best_score) {
518 best_score = tsi->tsi_SccLowlink;
522 if (best == NULL) /* done */
525 /* collect best candidate and all packages that get freed */
526 inner_queue_start = inner_queue_end = NULL;
527 addQ(best, &inner_queue_start, &inner_queue_end, prefcolor);
529 for (; inner_queue_start != NULL;
530 inner_queue_start = inner_queue_start->tsi_suc) {
531 /* Mark the package as unqueued. */
532 inner_queue_start->tsi_reqx = 0;
533 collectTE(prefcolor, inner_queue_start, newOrder, newOrderCount,
534 SCCs, &inner_queue_end, &outer_queue_start, queue_end);
538 /* restore outer queue */
539 p_tsi->tsi_suc = outer_queue_start;
542 int rpmtsOrder(rpmts ts)
544 tsMembers tsmem = rpmtsMembers(ts);
545 rpm_color_t prefcolor = rpmtsPrefColor(ts);
549 int newOrderCount = 0;
551 rpmal erasedPackages;
553 int nelem = rpmtsNElements(ts);
554 tsortInfo sortInfo = xcalloc(nelem, sizeof(struct tsortInfo_s));
555 ARGV_t seenColls = NULL;
557 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_ORDER), 0);
559 /* Create erased package index. */
560 erasedPackages = rpmtsCreateAl(ts, TR_REMOVED);
562 for (int i = 0; i < nelem; i++) {
563 sortInfo[i].te = tsmem->order[i];
564 rpmteSetTSI(tsmem->order[i], &sortInfo[i]);
567 /* Record relations. */
568 rpmlog(RPMLOG_DEBUG, "========== recording tsort relations\n");
570 while ((p = rpmtsiNext(pi, 0)) != NULL) {
571 rpmal al = (rpmteType(p) == TR_REMOVED) ?
572 erasedPackages : tsmem->addedPackages;
573 rpmds requires = rpmdsInit(rpmteDS(p, RPMTAG_REQUIRENAME));
574 rpmds order = rpmdsInit(rpmteDS(p, RPMTAG_ORDERNAME));
576 while (rpmdsNext(requires) >= 0) {
577 /* Record next "q <- p" relation (i.e. "p" requires "q"). */
578 (void) addRelation(ts, al, p, requires);
581 while (rpmdsNext(order) >= 0) {
582 /* Record next "q <- p" ordering request */
583 (void) addRelation(ts, al, p, order);
586 addCollRelations(al, p, &seenColls);
589 seenColls = argvFree(seenColls);
592 newOrder = xcalloc(tsmem->orderCount, sizeof(*newOrder));
593 SCCs = detectSCCs(sortInfo, nelem, (rpmtsFlags(ts) & RPMTRANS_FLAG_DEPLOOPS));
595 rpmlog(RPMLOG_DEBUG, "========== tsorting packages (order, #predecessors, #succesors, depth)\n");
597 for (int i = 0; i < 2; i++) {
598 /* Do two separate runs: installs first - then erases */
599 int oType = !i ? TR_ADDED : TR_REMOVED;
601 /* Scan for zeroes and add them to the queue */
602 for (int e = 0; e < nelem; e++) {
603 tsortInfo p = &sortInfo[e];
604 if (rpmteType(p->te) != oType) continue;
605 if (p->tsi_count != 0)
608 addQ(p, &q, &r, prefcolor);
611 /* Add one member of each leaf SCC */
612 for (int i = 2; SCCs[i].members != NULL; i++) {
613 tsortInfo member = SCCs[i].members[0];
614 if (SCCs[i].count == 0 && rpmteType(member->te) == oType) {
615 addQ(member, &q, &r, prefcolor);
620 /* Mark the package as unqueued. */
622 if (q->tsi_SccIdx > 1) {
623 collectSCC(prefcolor, q, newOrder, &newOrderCount, SCCs, &r);
625 collectTE(prefcolor, q, newOrder, &newOrderCount, SCCs, &r,
632 /* Clean up tsort data */
633 for (int i = 0; i < nelem; i++) {
634 rpmteSetTSI(tsmem->order[i], NULL);
635 rpmTSIFree(&sortInfo[i]);
639 assert(newOrderCount == tsmem->orderCount);
641 tsmem->order = _free(tsmem->order);
642 tsmem->order = newOrder;
643 tsmem->orderAlloced = tsmem->orderCount;
646 for (int i = 2; SCCs[i].members != NULL; i++) {
647 free(SCCs[i].members);
650 rpmalFree(erasedPackages);
652 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_ORDER), 0);