rpm-build/tools/rpmsort.c

580 lines
14 KiB
C

#include "system.h"
#include "rpmlib.h"
#include "rpmmacro.h"
#include "rpmurl.h"
#include "depends.h"
#include "manifest.h"
#include "misc.h"
#include "debug.h"
static int _depends_debug;
static int noAvailable = 1;
static const char * avdbpath =
"/usr/lib/rpmdb/%{_arch}-%{_vendor}-%{_os}/redhat";
static int noChainsaw = 0;
static int noDeps = 0;
/**
* Compare two available index entries by name (qsort/bsearch).
* @param one 1st available index entry
* @param two 2nd available index entry
* @return result of comparison
*/
static int indexcmp(const void * one, const void * two) /*@*/
{
const struct availableIndexEntry * a = one;
const struct availableIndexEntry * b = two;
int lenchk = a->entryLen - b->entryLen;
if (lenchk)
return lenchk;
return strcmp(a->entry, b->entry);
}
/**
* Compare two directory info entries by name (qsort/bsearch).
* @param one 1st directory info
* @param two 2nd directory info
* @return result of comparison
*/
static int dirInfoCompare(const void * one, const void * two) /*@*/
{
const dirInfo a = (const dirInfo) one;
const dirInfo b = (const dirInfo) two;
int lenchk = a->dirNameLen - b->dirNameLen;
if (lenchk)
return lenchk;
/* XXX FIXME: this might do "backward" strcmp for speed */
return strcmp(a->dirName, b->dirName);
}
/**
* Check added package file lists for package(s) that provide a file.
* @param al available list
* @param keyType type of dependency
* @param fileName file name to search for
* @return available package pointer
*/
static /*@only@*/ /*@null@*/ struct availablePackage **
alAllFileSatisfiesDepend(const availableList al,
const char * keyType, const char * fileName)
/*@*/
{
int i, found;
const char * dirName;
const char * baseName;
struct dirInfo_s dirNeedle;
dirInfo dirMatch;
struct availablePackage ** ret;
/* Solaris 2.6 bsearch sucks down on this. */
if (al->numDirs == 0 || al->dirs == NULL || al->list == NULL)
return NULL;
{ char * t;
dirName = t = xstrdup(fileName);
if ((t = strrchr(t, '/')) != NULL) {
t++; /* leave the trailing '/' */
*t = '\0';
}
}
dirNeedle.dirName = (char *) dirName;
dirNeedle.dirNameLen = strlen(dirName);
dirMatch = bsearch(&dirNeedle, al->dirs, al->numDirs,
sizeof(dirNeedle), dirInfoCompare);
if (dirMatch == NULL) {
dirName = _free(dirName);
return NULL;
}
/* rewind to the first match */
while (dirMatch > al->dirs && dirInfoCompare(dirMatch-1, &dirNeedle) == 0)
dirMatch--;
/*@-nullptrarith@*/ /* FIX: fileName NULL ??? */
baseName = strrchr(fileName, '/') + 1;
/*@=nullptrarith@*/
for (found = 0, ret = NULL;
dirMatch <= al->dirs + al->numDirs &&
dirInfoCompare(dirMatch, &dirNeedle) == 0;
dirMatch++)
{
/* XXX FIXME: these file lists should be sorted and bsearched */
for (i = 0; i < dirMatch->numFiles; i++) {
if (dirMatch->files[i].baseName == NULL ||
strcmp(dirMatch->files[i].baseName, baseName))
continue;
/*
* If a file dependency would be satisfied by a file
* we are not going to install, skip it.
*/
if (al->list[dirMatch->files[i].pkgNum].multiLib &&
!isFileMULTILIB(dirMatch->files[i].fileFlags))
continue;
if (keyType)
rpmMessage(RPMMESS_DEBUG, _("%s: %-45s YES (added files)\n"),
keyType, fileName);
ret = xrealloc(ret, (found+2) * sizeof(*ret));
if (ret) /* can't happen */
ret[found++] = al->list + dirMatch->files[i].pkgNum;
/*@innerbreak@*/ break;
}
}
dirName = _free(dirName);
/*@-mods@*/ /* FIX: al->list might be modified. */
if (ret)
ret[found] = NULL;
/*@=mods@*/
return ret;
}
/**
* Check added package file lists for package(s) that have a provide.
* @param al available list
* @param keyType type of dependency
* @param keyDepend dependency string representation
* @param keyName dependency name string
* @param keyEVR dependency [epoch:]version[-release] string
* @param keyFlags dependency logical range qualifiers
* @return available package pointer
*/
static /*@only@*/ /*@null@*/ struct availablePackage **
alAllSatisfiesDepend(const availableList al,
const char * keyType, const char * keyDepend,
const char * keyName, const char * keyEVR, int keyFlags)
/*@*/
{
struct availableIndexEntry needle, * match;
struct availablePackage * p, ** ret = NULL;
int i, rc, found;
if (*keyName == '/') {
ret = alAllFileSatisfiesDepend(al, keyType, keyName);
/* XXX Provides: /path was broken with added packages (#52183). */
if (ret != NULL && *ret != NULL)
return ret;
}
if (!al->index.size || al->index.index == NULL) return NULL;
needle.entry = keyName;
needle.entryLen = strlen(keyName);
match = bsearch(&needle, al->index.index, al->index.size,
sizeof(*al->index.index), indexcmp);
if (match == NULL) return NULL;
/* rewind to the first match */
while (match > al->index.index && indexcmp(match-1, &needle) == 0)
match--;
for (ret = NULL, found = 0;
match <= al->index.index + al->index.size &&
indexcmp(match, &needle) == 0;
match++)
{
p = match->package;
rc = 0;
switch (match->type) {
case IET_PROVIDES:
i = match->entryIx;
{ const char * proEVR;
int proFlags;
proEVR = (p->providesEVR ? p->providesEVR[i] : NULL);
proFlags = (p->provideFlags ? p->provideFlags[i] : 0);
rc = rpmRangesOverlap(p->provides[i], proEVR, proFlags,
keyName, keyEVR, keyFlags);
if (rc)
/*@innerbreak@*/ break;
}
if (keyType && keyDepend && rc)
rpmMessage(RPMMESS_DEBUG, _("%s: %-45s YES (added provide)\n"),
keyType, keyDepend+2);
break;
}
if (rc) {
ret = xrealloc(ret, (found + 2) * sizeof(*ret));
if (ret) /* can't happen */
ret[found++] = p;
}
}
if (ret)
ret[found] = NULL;
return ret;
}
/**
* Check added package file lists for first package that has a provide.
* @todo Eliminate.
* @param al available list
* @param keyType type of dependency
* @param keyDepend dependency string representation
* @param keyName dependency name string
* @param keyEVR dependency [epoch:]version[-release] string
* @param keyFlags dependency logical range qualifiers
* @return available package pointer
*/
static inline /*@only@*/ /*@null@*/ struct availablePackage *
alSatisfiesDepend(const availableList al,
const char * keyType, const char * keyDepend,
const char * keyName, const char * keyEVR, int keyFlags)
/*@*/
{
struct availablePackage * ret;
struct availablePackage ** tmp =
alAllSatisfiesDepend(al, keyType, keyDepend, keyName, keyEVR, keyFlags);
if (tmp) {
ret = tmp[0];
tmp = _free(tmp);
return ret;
}
return NULL;
}
static int
do_tsort(const char *fileArgv[])
{
const char * rootdir = "/";
rpmdb rpmdb = NULL;
rpmTransactionSet ts = NULL;
const char ** pkgURL = NULL;
char * pkgState = NULL;
const char ** fnp;
const char * fileURL = NULL;
int numPkgs = 0;
int numFailed = 0;
int prevx;
int pkgx;
const char ** argv = NULL;
int argc = 0;
const char ** av = NULL;
int ac = 0;
Header h;
int rc = 0;
int i;
if (fileArgv == NULL)
return 0;
rc = rpmdbOpen(rootdir, &rpmdb, O_RDONLY, 0644);
if (rc) {
rpmMessage(RPMMESS_ERROR, _("cannot open Packages database\n"));
rc = -1;
goto exit;
}
ts = rpmtransCreateSet(rpmdb, rootdir);
if (!noChainsaw)
ts->transFlags = RPMTRANS_FLAG_CHAINSAW;
/* Load all the available packages. */
if (!(noDeps || noAvailable)) {
rpmdbMatchIterator mi = NULL;
struct rpmdb_s * avdb = NULL;
addMacro(NULL, "_dbpath", NULL, avdbpath, RMIL_CMDLINE);
rc = rpmdbOpen(rootdir, &avdb, O_RDONLY, 0644);
delMacro(NULL, "_dbpath");
if (rc) {
rpmMessage(RPMMESS_ERROR, _("cannot open Available database\n"));
goto endavail;
}
mi = rpmdbInitIterator(avdb, RPMDBI_PACKAGES, NULL, 0);
while ((h = rpmdbNextIterator(mi)) != NULL) {
rpmtransAvailablePackage(ts, h, NULL);
}
endavail:
if (mi) rpmdbFreeIterator(mi);
if (avdb) rpmdbClose(avdb);
}
/* Build fully globbed list of arguments in argv[argc]. */
for (fnp = fileArgv; *fnp; fnp++) {
av = _free(av);
ac = 0;
rc = rpmGlob(*fnp, &ac, &av);
if (rc || ac == 0) continue;
argv = (argc == 0)
? xmalloc((argc+2) * sizeof(*argv))
: xrealloc(argv, (argc+2) * sizeof(*argv));
memcpy(argv+argc, av, ac * sizeof(*av));
argc += ac;
argv[argc] = NULL;
}
av = _free(av);
numPkgs = 0;
prevx = 0;
pkgx = 0;
restart:
/* Allocate sufficient storage for next set of args. */
if (pkgx >= numPkgs) {
numPkgs = pkgx + argc;
pkgURL = (pkgURL == NULL)
? xmalloc( (numPkgs + 1) * sizeof(*pkgURL))
: xrealloc(pkgURL, (numPkgs + 1) * sizeof(*pkgURL));
memset(pkgURL + pkgx, 0, ((argc + 1) * sizeof(*pkgURL)));
pkgState = (pkgState == NULL)
? xmalloc( (numPkgs + 1) * sizeof(*pkgState))
: xrealloc(pkgState, (numPkgs + 1) * sizeof(*pkgState));
memset(pkgState + pkgx, 0, ((argc + 1) * sizeof(*pkgState)));
}
/* Copy next set of args. */
for (i = 0; i < argc; i++) {
fileURL = _free(fileURL);
fileURL = argv[i];
argv[i] = NULL;
pkgURL[pkgx] = fileURL;
fileURL = NULL;
pkgx++;
}
fileURL = _free(fileURL);
/* Continue processing file arguments, building transaction set. */
for (fnp = pkgURL+prevx; *fnp; fnp++, prevx++) {
const char * fileName;
int isSource;
FD_t fd;
(void) urlPath(*fnp, &fileName);
/* Try to read the header from a package file. */
fd = Fopen(*fnp, "r.ufdio");
if (fd == NULL || Ferror(fd)) {
rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), *fnp,
Fstrerror(fd));
if (fd) Fclose(fd);
numFailed++; *fnp = NULL;
continue;
}
rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
Fclose(fd);
if (rc == 2) {
numFailed++; *fnp = NULL;
continue;
}
if (rc == 0) {
rc = rpmtransAddPackage(ts, h, NULL, fileName, 0, NULL);
headerFree(h); /* XXX reference held by transaction set */
continue;
}
if (rc != 1) {
rpmMessage(RPMMESS_ERROR, _("%s cannot be installed\n"), *fnp);
numFailed++; *fnp = NULL;
break;
}
/* Try to read a package manifest. */
fd = Fopen(*fnp, "r.fpio");
if (fd == NULL || Ferror(fd)) {
rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), *fnp,
Fstrerror(fd));
if (fd) Fclose(fd);
numFailed++; *fnp = NULL;
break;
}
/* Read list of packages from manifest. */
rc = rpmReadPackageManifest(fd, &argc, &argv);
if (rc)
rpmError(RPMERR_MANIFEST, _("%s: read manifest failed: %s\n"),
fileURL, Fstrerror(fd));
Fclose(fd);
/* If successful, restart the query loop. */
if (rc == 0) {
prevx++;
goto restart;
}
numFailed++; *fnp = NULL;
break;
}
if (numFailed) goto exit;
if (!noDeps) {
rpmDependencyConflict conflicts = NULL;
int numConflicts = 0;
rc = rpmdepCheck(ts, &conflicts, &numConflicts);
if (conflicts) {
rpmMessage(RPMMESS_ERROR, _("failed dependencies:\n"));
printDepProblems(stderr, conflicts, numConflicts);
conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
rc = -1;
goto exit;
}
}
rc = rpmdepOrder(ts);
if (rc)
goto exit;
{ int oc;
int npkgs = ts->orderCount;
struct availablePackage * p;
struct availablePackage * q;
unsigned char * selected = alloca(sizeof(*selected) * (npkgs + 1));
int i, j;
fprintf(stdout, "digraph XXX {\n");
for (oc = 0; oc < npkgs; oc++) {
p = NULL;
switch (ts->order[oc].type) {
case TR_ADDED:
i = ts->order[oc].u.addedIndex;
p = ts->addedPackages.list + ts->order[oc].u.addedIndex;
break;
case TR_REMOVED:
continue;
break;
}
fprintf(stdout, "\"%s\"\n", p->name);
}
for (oc = 0; oc < npkgs; oc++) {
int matchNum;
p = NULL;
switch (ts->order[oc].type) {
case TR_ADDED:
i = ts->order[oc].u.addedIndex;
p = ts->addedPackages.list + ts->order[oc].u.addedIndex;
break;
case TR_REMOVED:
continue;
break;
}
if (p->requiresCount <= 0)
continue;
memset(selected, 0, sizeof(*selected) * npkgs);
matchNum = p - ts->addedPackages.list;
selected[matchNum] = 1;
for (j = 0; j < p->requiresCount; j++) {
q = alSatisfiesDepend(&ts->addedPackages, NULL, NULL,
p->requires[j], p->requiresEVR[j], p->requireFlags[j]);
if (q == NULL)
continue;
if (!strncmp(p->requires[j], "rpmlib(", sizeof("rpmlib(")-1))
continue;
matchNum = q - ts->addedPackages.list;
if (selected[matchNum] != 0)
continue;
selected[matchNum] = 1;
fprintf(stdout, "\"%s\" -> \"%s\"\n", p->name, q->name);
}
}
fprintf(stdout, "}\n");
}
rc = 0;
exit:
if (ts) rpmtransFree(ts);
for (i = 0; i < numPkgs; i++) {
pkgURL[i] = _free(pkgURL[i]);
}
pkgState = _free(pkgState);
pkgURL = _free(pkgURL);
argv = _free(argv);
if (rpmdb) rpmdbClose(rpmdb);
return rc;
}
static struct poptOption optionsTable[] = {
{ "noavailable", '\0', 0, &noAvailable, 0, NULL, NULL},
{ "nochainsaw", '\0', 0, &noChainsaw, 0, NULL, NULL},
{ "nodeps", '\0', 0, &noDeps, 0, NULL, NULL},
{ "verbose", 'v', 0, 0, 'v', NULL, NULL},
{ NULL, 0, 0, 0, 0, NULL, NULL}
};
int
main(int argc, const char *argv[])
{
poptContext optCon;
const char * optArg;
int arg;
int ec = 0;
#if HAVE_MCHECK_H && HAVE_MTRACE
mtrace(); /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
#endif
setprogname(argv[0]); /* Retrofit glibc __progname */
(void)setlocale(LC_ALL, "" );
#ifdef __LCLINT__
#define LOCALEDIR "/usr/share/locale"
#endif
(void)bindtextdomain(PACKAGE, LOCALEDIR);
(void)textdomain(PACKAGE);
_depends_debug = 1;
optCon = poptGetContext("rpmsort", argc, argv, optionsTable, 0);
poptReadDefaultConfig(optCon, 1);
while ((arg = poptGetNextOpt(optCon)) > 0) {
optArg = poptGetOptArg(optCon);
switch (arg) {
case 'v':
rpmIncreaseVerbosity();
break;
default:
fprintf(stderr, _("unknown popt return (%d)"), arg);
exit (EXIT_FAILURE);
/*@notreached@*/ break;
}
}
rpmReadConfigFiles(NULL, NULL);
ec = do_tsort(poptGetArgs(optCon));
optCon = poptFreeContext(optCon);
return ec;
}