/*
* rpmfind.c : Minimalist locate and transfer module for FTP and HTTP requests
*
* See Copyright for the status of this software.
*
* $Id: rpmfind.c,v 1.20 1998/08/20 04:53:44 veillard Exp $
*/
#include "config.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <dirent.h>
#include <errno.h>
#include <time.h>
#ifdef HAVE_REGEX_H
#include <regex.h>
#else
#ifdef HAVE_RXPOSIX_H
#include <rxposix.h>
#endif
#endif
#include <rpm/rpmlib.h>
#include "rpmdata.h"
#include "rdf_api.h"
#include "deps.h"
#include "language.h"
#include "transport.h"
#include "database.h"
#include "rpmfind.h"
/*
* Global variables.
*/
char *myServer = "http://rufus.w3.org/linux/RDF/";
int rpmfindVerbose = 1;
int rpmfindMode = RPM_FIND_LOOKUP;
int rpmfindMirrorUpgrade = 1;
int rpmfindUseOrigin = 0;
char *rpmfindTempDir = NULL;
/*
* Ftp informations
*/
char *downloadDir = "/tmp";
/*
* Informations on the current setup.
*/
char *myPrefix = "/";
char *myArch = NULL;
char *myOs = NULL;
char *myVendor = "Red Hat Software";
char *myDistribution = "Hurricane";
int nbInstalledPackages = 0;
static rpmdb myDb;
time_t myTime = 0;
/****************************************************************
* *
* NAME PARSER *
* *
****************************************************************/
/*
* Truncate a resource name, trying to extract name-version-release
*/
void tokenizeName(char *resource, char **name, char **version,
char **release) {
static char myName[200];
static char myVersion[200];
static char myRelease[200];
char *cur;
char *out;
cur = resource;
myName[0] = '\0';
myVersion[0] = '\0';
myRelease[0] = '\0';
*version = NULL;
*release = NULL;
*name = &myName[0];
out = &myName[0];
while (1) {
if (*cur == '\0') {
*out = '\0';
return;
}
if (*cur == '-') {
cur++;
if ((*cur >= '0') && (*cur <= '9')) {
*out = '\0';
break;
}
*out++ = '-';
}
*out++ = *cur++;
}
*version = &myVersion[0];
out = &myVersion[0];
while (1) {
if (*cur == '\0') {
*out = '\0';
return;
}
if (*cur == '-') {
*out = '\0';
break;
}
*out++ = *cur++;
}
*release = &myRelease[0];
out = &myRelease[0];
while (1) {
if (*cur == '\0') {
*out = '\0';
return;
}
*out++ = *cur++;
}
}
/****************************************************************
* *
* NETWORK CACHE *
* *
****************************************************************/
/*
* The files collected from the net
* This act as a cache for informations already collected from
* the network, this is primordial to not ask continously metadata
* for the same packages.
*/
int nbNetResources = 0;
typedef struct netResource {
struct netResource *next; /* link to next in list */
struct netResource *prev; /* link to prev in list */
char *URL; /* The URL of the resource */
char *filename; /* The filename containing the content */
} netResource, *netResourcePtr;
static netResourcePtr netResourceList = NULL;
int findResource(neededPackagePtr father, char *resource);
/*
* Add a new net resource.
*/
void netResourceAdd(char *URL, char *filename) {
netResourcePtr cur;
cur = (netResourcePtr) malloc(sizeof(netResource));
if (cur == NULL) {
fprintf(stderr, "netResourceAdd : ran out of memory !\n");
exit(1);
}
cur->URL = strdup(URL);
if (filename != NULL)
cur->filename = strdup(filename);
else
cur->filename = NULL;
cur->next = netResourceList;
cur->prev = NULL;
if (cur->next != NULL)
cur->next->prev = cur;
netResourceList = cur;
}
/*
* Search a net resource.
*/
char *netResourceSearch(char *URL) {
netResourcePtr cur;
cur = netResourceList;
while (cur != NULL) {
if (!strcmp(URL, cur->URL)) return(cur->filename);
cur = cur->next;
}
return(NULL);
}
/*
* Cleanup the net resources.
*/
void netResourceClean(void) {
netResourcePtr cur, next;
cur = netResourceList;
while (cur != NULL) {
next = cur->next;
if (cur->filename != NULL) {
unlink(cur->filename);
free(cur->filename);
}
free(cur->URL);
cur->URL = NULL;
free(cur);
cur = next;
}
}
/****************************************************************
* *
* PACKAGE SELECTION *
* *
****************************************************************/
/*
* Give a score for a package version number.
* e.g. 1.1.2 returns 1,001,002
* 1.2beta3 " 1,002,000
*/
int rpmFindVersionValue(const char *version) {
const char *cur = version;
int res = 0;
int val = 0;
int multiplier = 1000 * 1000;
while (*cur != '\0') {
if ((*cur >= '0') && (*cur <= '9'))
val = val * 10 + (*cur - '0');
else
break;
cur++;
}
if (val >= 70) goto done;
res += val * multiplier;
if (*cur != '.') goto done;
val = 0;
multiplier /= 1000;
cur++;
while (*cur != '\0') {
if ((*cur >= '0') && (*cur <= '9'))
val = val * 10 + (*cur - '0');
else
break;
cur++;
}
if (val >= 1000) goto done;
res += val * multiplier;
if (*cur != '.') goto done;
val = 0;
multiplier /= 1000;
cur++;
while (*cur != '\0') {
if ((*cur >= '0') && (*cur <= '9'))
val = val * 10 + (*cur - '0');
else
break;
cur++;
}
if (val >= 1000) goto done;
res += val * multiplier;
done:
return(res);
}
/*
* Do a package info evaluation, criteria:
* - Where does it come from (match myVendor/myDistribution)
affinity w.r.t. father and current setup
* - version/release numbers
* - how old, give priority to (very) recent packages
*/
void neededPackageEvaluate(neededPackagePtr father, neededPackagePtr cur) {
int rating = 0;
int distrib_rating = 0;
unsigned long basetime;
unsigned long delta;
const char *ID;
/*
* Get the distribution rating, if any...
*/
ID = rpmPackageFindDistrib(cur);
if (ID != NULL)
distrib_rating = rpmDistribGetRating(ID);
if (distrib_rating < 0) {
cur->rating = 0;
return;
}
if (rpmfindMode == RPM_FIND_LATEST) {
cur->rating = rpmFindVersionValue(cur->version);
return;
} else {
rating = rpmFindVersionValue(cur->version) / 1000;
}
cur->rating += distrib_rating;
if (father != NULL) {
if (!strcmp(cur->vendor, father->vendor)) {
rating += MED_RATING;
if (!strcmp(cur->distribution, father->distribution))
rating += HIGH_RATING;
}
if (!strcmp(cur->vendor, myVendor)) {
rating += LOW_RATING;
if (!strcmp(cur->distribution, myDistribution))
rating += MED_RATING;
}
} else {
if (!strcmp(cur->vendor, myVendor)) {
rating += MED_RATING;
if (!strcmp(cur->distribution, myDistribution))
rating += HIGH_RATING;
}
}
if (rpmfindVerbose > 2)
printf("vendor rating : %d\n", rating);
if (father == NULL)
basetime = myTime;
else
basetime = father->date;
if (cur->date < basetime) delta = basetime - cur->date;
else delta = cur->date - basetime;
delta = HIGH_RATING * MAX_DAY * 2 /
((HIGH_RATING * delta) / (24 * 60 * 60 * 10) + MAX_DAY);
rating += delta;
if (rpmfindVerbose > 2)
printf("date rating : %d\n", (int) delta);
if (cur->size > 1000) {
rating += (HIGH_RATING * 1024) / cur->size;
if (rpmfindVerbose > 2)
printf("size rating : %d\n",
(HIGH_RATING * 1024) / cur->size);
}
cur->rating = rating;
}
/*
* Check-up architecture and Os compatibilities
*/
int neededPackageCompatible(neededPackagePtr cur) {
if (!strcasecmp(cur->arch, "noarch")) return(1);
if (strcasecmp(cur->os, myOs)) return(0);
if (strcasecmp(cur->arch, myArch)) {
/*
* IAPX86 familly compatibility chart.
*/
if (((myArch[0] == 'i') && (cur->arch[0] == 'i')) &&
((myArch[2] == '8') && (cur->arch[2] == '8')) &&
((myArch[3] == '6') && (cur->arch[3] == '6'))) {
/* ascending compatibility */
if (cur->arch[1] <= myArch[1]) return(1);
/* 386 accept 486 optimized binaries */
if ((cur->arch[1] == '4') && (myArch[1] == '3')) return(1);
/* 586 accept 686 optimized binaries */
if ((cur->arch[1] == '6') && (myArch[1] == '5')) return(1);
}
return(0);
}
return(1);
}
/*
* Comparision funtion for sorting.
*/
typedef int (*neededPackageCmpFunc)
(const neededPackagePtr a, const neededPackagePtr b);
int neededPackageCmpRating(const neededPackagePtr *a, const neededPackagePtr *b) {
if ((*a == NULL) && (*b == NULL)) return(0);
if (*a == NULL) return(+1);
if (*b == NULL) return(-1);
return((*b)->rating - (*a)->rating);
}
/*
* Cleanup a list of neededPackage structures, remove wrong arch and
* wrong os, then set-up preferences w.r.t the current setup, timestamp
* and user's preferences.
*/
void neededPackageSelect(neededResourcePtr res, neededPackagePtr father) {
int i;
neededPackagePtr cur;
/*
* Walk the list, remove invalid packages and give an
* value for the package.
*/
for (i = 0;i < res->nb_pkgs;i++) {
cur = res->pkgs[i];
/*
* Test for package removal.
*/
if (!neededPackageCompatible(cur)) {
if (rpmfindVerbose > 1)
printf("removing %s-%s-%s, wrong platform %s/%s\n",
cur->name, cur->version, cur->release, cur->os, cur->arch);
res->pkgs[i]->rating = -1;
} else {
neededPackageEvaluate(father, cur);
}
}
/*
* Now sort the list by rating.
*/
qsort(res->pkgs, res->nb_pkgs, sizeof(neededPackagePtr),
(int (*)(const void *, const void *)) neededPackageCmpRating);
}
/****************************************************************
* *
* RDF METADATA INTERFACE *
* *
****************************************************************/
/*
* Parse a resource RDF info file and setup the package list
* of a neededResource structure.
*/
void neededResourceParseRdf(neededResourcePtr res, const char *file) {
rdfSchema rdf;
rdfNamespace rpmNs;
rdfNamespace rdfNs;
rdfDescription desc;
char *value;
char *filename;
char *URL;
neededPackagePtr info = NULL;
int i;
rdf = rdfRead(file);
if (rdf == NULL) return;
/*
* Start the analyze, check that's an RDF for RPM packages.
*/
rdfNs = rdfGetNamespace(rdf, "http://www.w3.org/TR/WD-rdf-syntax#");
if (rdfNs == NULL) {
if (rpmfindVerbose)
printf("%s is not an RDF schema\n", file);
rdfDestroySchema(rdf);
return;
}
rpmNs = rdfGetNamespace(rdf, "http://www.rpm.org/");
if (rdfNs == NULL) {
if (rpmfindVerbose)
printf("%s is not an RPM specific RDF schema\n", file);
rdfDestroySchema(rdf);
return;
}
desc = rdfFirstDescription(rdf);
if (rdfNs == NULL) {
if (rpmfindVerbose)
printf("%s RDF schema seems empty\n", file);
rdfDestroySchema(rdf);
return;
}
/*
* We are pretty sure that it will be a valid schema,
* Walk the tree and collect the packages descriptions.
*/
while (desc != NULL) {
/*
* Get the resource URL ...
*/
value = rdfGetDescriptionHref(rdf, desc);
if (value != NULL) URL = strdup(value);
else {
if (rpmfindVerbose)
printf("%s RDF schema invalid : Desc without href\n",
file);
rdfDestroySchema(rdf);
return;
}
filename = &value[strlen(value)];
while ((filename > value) && (*filename != '/'))
filename--;
/*
* if already registered, add it directly and skip to next one.
*/
info = neededPackageSearch(URL);
if (info != NULL) {
neededResourceAddPackage(res, info);
desc = rdfNextDescription(desc);
continue;
}
/*
* Allocate the neededPackage structure, initialize it.
*/
info = (neededPackagePtr) malloc(sizeof(neededPackage));
if (info == NULL) {
fprintf(stderr, "rpmOpenRdf : out of memory !\n");
rdfDestroySchema(rdf);
return;
}
memset(info, 0, sizeof(neededPackage));
info->rpmdata = NULL;
info->mirror = -1;
info->distribNr = -1;
info->status = STATUS_NONE;
info->refcount = 0;
if (filename != value) {
filename++;
info->filename = strdup(filename);
} else
info->filename = NULL;
info->dir = NULL; /* Computed only at fetch time if needed */
/*
* Now extract all the metadata informations from the RDF tree
*/
rdfGetValue(desc, "Name", rpmNs, &value, NULL);
if (value != NULL) info->name = strdup(value);
else {
if (rpmfindVerbose)
printf("%s RDF schema invalid : no Name\n", file);
rdfDestroySchema(rdf);
return;
}
rdfGetValue(desc, "Version", rpmNs, &value, NULL);
if (value != NULL) info->version = strdup(value);
else {
if (rpmfindVerbose)
printf("%s RDF schema invalid : no Version\n", file);
rdfDestroySchema(rdf);
return;
}
rdfGetValue(desc, "Release", rpmNs, &value, NULL);
if (value != NULL) info->release = strdup(value);
else {
if (rpmfindVerbose)
printf("%s RDF schema invalid : no Release\n", file);
rdfDestroySchema(rdf);
return;
}
rdfGetValue(desc, "Arch", rpmNs, &value, NULL);
if (value != NULL) info->arch = strdup(value);
else info->arch = strdup(localizedStrings[LANG_NONE]);
rdfGetValue(desc, "Os", rpmNs, &value, NULL);
if (value != NULL) info->os = strdup(value);
else info->os = strdup("linux");
rdfGetValue(desc, "Distribution", rpmNs, &value, NULL);
if (value != NULL) info->distribution = strdup(value);
else info->distribution = strdup(localizedStrings[LANG_UNKNOWN]);
rdfGetValue(desc, "Vendor", rpmNs, &value, NULL);
if (value != NULL) info->vendor = strdup(value);
else info->vendor = strdup(localizedStrings[LANG_UNKNOWN]);
rdfGetValue(desc, "Date", rpmNs, &value, NULL);
if (value != NULL) {
if (sscanf(value, "%d", &i) != 1)
info->date = 0;
else
info->date = i;
} else info->date = 0;
rdfGetValue(desc, "Size", rpmNs, &value, NULL);
if (value != NULL) {
if (sscanf(value, "%d", &i) != 1)
info->size = 0;
else
info->size = i;
} else info->size = 0;
rdfGetValue(desc, "Subdir", rpmNs, &value, NULL);
if (value != NULL) info->subdir = strdup(value);
else info->subdir = strdup(localizedStrings[LANG_UNKNOWN]);
info->URL = URL;
/*
* Link in the new list;
*/
neededResourceAddPackage(res, info);
/*
* Skip to next Description in the RDf file.
*/
desc = rdfNextDescription(desc);
}
/*
* Cleanup.
*/
rdfDestroySchema(rdf);
}
/*
* Do an apropos search in the whole catalog
*/
void aproposSearchRdf(const char *search, const char *catalog) {
rdfSchema rdf;
rdfNamespace rpmNs;
rdfNamespace rdfNs;
rdfDescription desc;
char *value;
char *URL;
char *name;
char *description;
int nbhits = 0;
#ifdef HAVE_REGEX_H
regex_t reg;
regmatch_t pmatch[1];
#endif
rdf = rdfRead(catalog);
if (rdf == NULL) {
fprintf(stderr, "Cannot open catalog %s\n", catalog);
return;
}
/*
* Start the analyze, check that's an RDF for RPM packages.
*/
rdfNs = rdfGetNamespace(rdf, "http://www.w3.org/TR/WD-rdf-syntax#");
if (rdfNs == NULL) {
if (rpmfindVerbose)
printf("%s is not an RDF schema\n", catalog);
rdfDestroySchema(rdf);
return;
}
rpmNs = rdfGetNamespace(rdf, "http://www.rpm.org/");
if (rdfNs == NULL) {
if (rpmfindVerbose)
printf("%s is not an RPM specific RDF schema\n", catalog);
rdfDestroySchema(rdf);
return;
}
desc = rdfFirstDescription(rdf);
if (rdfNs == NULL) {
if (rpmfindVerbose)
printf("%s RDF schema seems empty\n", catalog);
rdfDestroySchema(rdf);
return;
}
#ifdef HAVE_REGEX_H
#ifdef REG_ICASE
regcomp(®, search, REG_ICASE);
#else
regcomp(®, search, 0);
#endif
#endif
/*
* Search the string in the package name and descriptions.
*/
while (desc != NULL) {
/*
* Get the resource URL ...
*/
value = rdfGetDescriptionHref(rdf, desc);
if (value != NULL) URL = value;
else {
if (rpmfindVerbose)
printf("%s RDF schema invalid : Desc without href\n",
catalog);
rdfDestroySchema(rdf);
return;
}
/*
* Now extract all the metadata informations from the RDF tree
*/
rdfGetValue(desc, "Name", rpmNs, &value, NULL);
if (value != NULL) name = value;
else {
if (rpmfindVerbose)
printf("%s RDF schema invalid : no Name\n", catalog);
rdfDestroySchema(rdf);
return;
}
rdfGetValue(desc, "Summary", rpmNs, &value, NULL);
if (value != NULL) description = value;
else description = "";
/*
* Search the regex/string.
*/
#ifdef HAVE_REGEX_H
if ((!regexec(®, name, 1, pmatch, 0)) ||
(!regexec(®, description, 1, pmatch, 0)))
#elif HAVE_STRSTR
if ((strstr(name, search)) || (strstr(description, search)))
#else
#error "Code currently relies on regex(3) or strstr(3) availability ..."
#endif
printf("%d: %s\n %s : %s\n\n", ++nbhits, URL, name, description);
/*
* Skip to next Description in the RDf catalog.
*/
desc = rdfNextDescription(desc);
}
/*
* Cleanup.
*/
rdfDestroySchema(rdf);
#ifdef HAVE_REGEX_H
regfree(®);
#endif
printf("Found %d packages related to %s\n", nbhits, search);
}
/****************************************************************
* *
* NETWORK INTERFACES *
* *
****************************************************************/
rpmTransportRequestPtr runningRequests = NULL;
/*
* Callback from the transport layer when a request has completed.
*/
int handleTransportEnd(rpmTransportRequestPtr req) {
printf("handleTransportEnd :");
runningRequestPrint(req);
return(0);
}
/*
* Load the apropos catalog, it's an RDf file but compressed for network
* efficiency w.r.t. PPP users.
*/
char *loadAproposCatalog(void) {
static char filename[1000];
char URL[1500];
char msg[100];
struct stat buf;
int res;
time_t current_time = time(NULL);
/* the catalog is stored along with the RPM fetched from the net */
sprintf(filename, "%s/fullIndex.rdf.gz", rpmfindTempDir);
/* Check file availability and age */
res = stat(filename, &buf);
if ((!res) && (buf.st_size > 1000)) {
if (current_time - buf.st_mtime > 24 * 60 * 60) {
printf("The index is %d day(s) old, refetch [Y/n] : ",
(int) ((current_time - buf.st_mtime) / (24 * 60 * 60)));
fflush(stdout);
res = scanf("%s", &msg[0]);
if (res == 1) {
switch (msg[0]) {
case 'n':
case 'N': return(filename);
}
}
} else
return(filename);
}
/* do the download ... */
sprintf(URL, "%s/resources/fullIndex.rdf.gz", myServer);
printf("Loading catalog to %s\n", filename);
res = fetchURLToFile(URL, filename);
if (res != 0) return(NULL);
return(filename);
}
/*
* Find a remote package providing a given resource.
* Get the metadata for the ressources, then walk the package list
* finding one to install on this system.
*/
int lookupRemoteResource(neededPackagePtr father, char *resource) {
char *name;
char *ver;
char *rel;
neededPackagePtr cur;
char URL[500];
char *filename;
rpmDataPtr rpmdata;
neededResourcePtr res;
int i;
int size = 0;
int val;
char fullname[200];
tokenizeName(resource, &name, &ver, &rel);
/*
* Check for the list of resources we refuse to upgrade.
*/
if (rpmNoUpgradeCheck(resource)) {
if (rpmfindVerbose > 1)
printf("Refuses upgrade of %s\n", resource);
return(-1);
}
/*
* Check for the list of resources we refuse to depend on.
*/
if (rpmNoDependCheck(resource)) {
if (rpmfindVerbose > 1)
printf("Refuses dependency of %s\n", resource);
return(-1);
}
if (rpmfindVerbose > 1)
printf("lookupRemoteResource %s\n", resource);
/*
* Do the most selective request first.
*/
if (rel != NULL) {
/*
* Has this resource already been looked at ?
*/
res = neededResourceSearch(resource);
if (res != NULL) {
neededPackageAddResource(father, res);
return(0);
}
/*
* Ok, start fetching Metadata !
*/
sprintf(URL, "%s/resources/%s.rdf", myServer, resource);
filename = fetchURL(URL);
if (filename == NULL) {
if (rpmfindVerbose)
printf("Error fetching %s metadata\n", resource);
goto search_no_rel;
}
res = neededResourceAdd(fullname, father);
neededResourceParseRdf(res, filename);
neededPackageSelect(res, father);
if (rpmfindVerbose > 1) {
neededResourcePrintPkgs(res);
}
/*
* Check every possible package.
*/
for (i = 0;i < res->nb_pkgs;i++) {
cur = res->pkgs[i];
size = 0;
if (cur == NULL) continue;
if (cur->rating < 0) continue;
if (cur->status == STATUS_TO_INSTALL) return(0);
if (cur->status == STATUS_UNINSTALLABLE) continue;
/*
* Can we actually install this package on this system.
*/
if (cur->rpmdata == NULL) {
cur->status = STATUS_LOOKUP;
sprintf(URL, "%s/%s/%s-%s-%s.%s.rdf",
myServer, cur->subdir, cur->name, cur->version,
cur->release, cur->arch);
filename = fetchURL(URL);
if (filename != NULL) {
cur->status = STATUS_LOOKUP;
rpmdata = rpmOpenRdfFile(filename);
cur->rpmdata = rpmdata;
size += cur->size;
}
}
if (cur->rpmdata == NULL) {
cur->status = STATUS_UNINSTALLABLE;
continue;
}
rpmdata = cur->rpmdata;
/*
* Now check the resources needed by this package.
*/
for (i = 0;i < rpmdata->nb_requires;i++) {
if (!strcmp(resource, rpmdata->requires[i]))
continue;
/*
* Check for the list of unacceptable dependencies
*/
if (rpmNoDependCheck(rpmdata->requires[i])) {
if (rpmfindVerbose > 1)
printf(
"Refuses package %s-%s-%s.%s.rpm: depends on %s\n",
cur->name, cur->version, cur->release,
cur->arch, rpmdata->requires[i]);
continue;
}
val = findResource(cur, rpmdata->requires[i]);
if (val < 0) {
cur->status = STATUS_UNINSTALLABLE;
neededPackageDereference(cur);
break;
}
size += val;
}
if (i >= rpmdata->nb_requires) {
cur->status = STATUS_TO_INSTALL;
return(size);
}
cur->status = STATUS_UNINSTALLABLE;
}
}
search_no_rel:
/*
* Do the most selective request first.
*/
if (ver != NULL) {
sprintf(fullname, "%s-%s", name, ver);
/*
* Has this resource already been looked at ?
*/
res = neededResourceSearch(fullname);
if (res != NULL) {
neededPackageAddResource(father, res);
return(0);
}
/*
* Ok, start fetching Metadata !
*/
sprintf(URL, "%s/resources/%s.rdf", myServer, fullname);
filename = fetchURL(URL);
if (filename == NULL) {
if (rpmfindVerbose)
printf("Error fetching %s metadata\n", fullname);
goto search_no_ver;
}
res = neededResourceAdd(fullname, father);
neededResourceParseRdf(res, filename);
neededPackageSelect(res, father);
if (rpmfindVerbose > 1) {
neededResourcePrintPkgs(res);
}
/*
* Check every possible package.
*/
for (i = 0;i < res->nb_pkgs;i++) {
cur = res->pkgs[i];
size = 0;
if (cur == NULL) continue;
if (cur->rating < 0) continue;
if (cur->status == STATUS_TO_INSTALL) return(0);
if (cur->status == STATUS_UNINSTALLABLE) continue;
/*
* Check that the release # match
*/
if ((rel != NULL) && (strcmp(rel, cur->release))) continue;
/*
* Can we actually install this package on this system.
*/
if (cur->rpmdata == NULL) {
cur->status = STATUS_LOOKUP;
sprintf(URL, "%s/%s/%s-%s-%s.%s.rdf",
myServer, cur->subdir, cur->name, cur->version,
cur->release, cur->arch);
filename = fetchURL(URL);
if (filename != NULL) {
cur->status = STATUS_LOOKUP;
rpmdata = rpmOpenRdfFile(filename);
cur->rpmdata = rpmdata;
size += cur->size;
}
}
if (cur->rpmdata == NULL) {
cur->status = STATUS_UNINSTALLABLE;
continue;
}
rpmdata = cur->rpmdata;
/*
* Now check the resources needed by this package.
*/
for (i = 0;i < rpmdata->nb_requires;i++) {
if (!strcmp(fullname, rpmdata->requires[i]))
continue;
val = findResource(cur, rpmdata->requires[i]);
if (val < 0) {
cur->status = STATUS_UNINSTALLABLE;
neededPackageDereference(cur);
break;
}
size += val;
}
if (i >= rpmdata->nb_requires) {
cur->status = STATUS_TO_INSTALL;
return(size);
}
cur->status = STATUS_UNINSTALLABLE;
}
}
search_no_ver:
/*
* Has this resource already been looked at ?
*/
res = neededResourceSearch(name);
if (res != NULL) {
neededPackageAddResource(father, res);
return(0);
}
/*
* Ok, start fetching Metadata !
*/
sprintf(URL, "%s/resources/%s.rdf", myServer, name);
filename = fetchURL(URL);
if (filename == NULL) {
if (rpmfindVerbose)
printf("Error fetching %s metadata\n", name);
return(-1);
}
res = neededResourceAdd(name, father);
neededResourceParseRdf(res, filename);
neededPackageSelect(res, father);
if (rpmfindVerbose > 1) {
neededResourcePrintPkgs(res);
}
/*
* Check every possible package.
*/
for (i = 0;i < res->nb_pkgs;i++) {
cur = res->pkgs[i];
size = 0;
if (cur == NULL) continue;
if (cur->rating < 0) continue;
if (cur->status == STATUS_TO_INSTALL) return(0);
if (cur->status == STATUS_UNINSTALLABLE) continue;
/*
* Check that the release and version # match
*/
if ((rel != NULL) && (strcmp(rel, cur->release))) continue;
if ((ver != NULL) && (strcmp(ver, cur->version))) continue;
/*
* Can we actually install this package on this system.
*/
if (cur->rpmdata == NULL) {
cur->status = STATUS_LOOKUP;
sprintf(URL, "%s/%s/%s-%s-%s.%s.rdf",
myServer, cur->subdir, cur->name, cur->version,
cur->release, cur->arch);
filename = fetchURL(URL);
if (filename != NULL) {
cur->status = STATUS_LOOKUP;
rpmdata = rpmOpenRdfFile(filename);
cur->rpmdata = rpmdata;
size += cur->size;
}
}
if (cur->rpmdata == NULL) {
cur->status = STATUS_UNINSTALLABLE;
continue;
}
rpmdata = cur->rpmdata;
/*
* Now check the resources needed by this package.
*/
for (i = 0;i < rpmdata->nb_requires;i++) {
if (!strcmp(name, rpmdata->requires[i]))
continue;
val = findResource(cur, rpmdata->requires[i]);
if (val < 0) {
cur->status = STATUS_UNINSTALLABLE;
neededPackageDereference(cur);
break;
}
size += val;
}
if (i >= rpmdata->nb_requires) {
cur->status = STATUS_TO_INSTALL;
return(size);
}
cur->status = STATUS_UNINSTALLABLE;
}
return(-1);
}
/****************************************************************
* *
* RPM DATABASE INTERFACE *
* *
****************************************************************/
/*
* Find a package, or a resource.
* One first check whether it's provided by one of the installed packages
*/
int findResource(neededPackagePtr father, char *resource) {
char * name = NULL, * version = NULL, * release = NULL;
int type, count, i;
dbiIndexSet matches;
struct stat buf;
int res;
int found = -1;
neededResourcePtr lookup;
Header h = NULL;
if (rpmfindVerbose > 1) {
printf("findResource %s\n", resource);
}
/*
* Check for the list of resources we refuse to depend on.
*/
if (rpmNoDependCheck(resource)) {
if (rpmfindVerbose > 1)
printf("Refuses dependency of %s\n", resource);
return(-1);
}
if ((resource[0] == '/') && (!stat(resource, &buf))) {
if (rpmfindVerbose > 1)
printf("Resource %s exists\n", resource);
found = 0;
} else if (rpmdbFindByProvides(myDb, resource, &matches) == 0) {
int j;
if (rpmfindVerbose > 1)
printf("Resource %s is provided by:", resource);
for (j = 0; j < matches.count; j++) {
if (matches.recs[j].recOffset) {
h = rpmdbGetRecord(myDb, matches.recs[j].recOffset);
if (!h) {
fprintf(stderr,
"error: could not read database record\n");
} else {
/* extract informations from the header */
headerGetEntry(h, RPMTAG_NAME, &type,
(void **) &name, &count);
headerGetEntry(h, RPMTAG_VERSION, &type,
(void **) &version, &count);
headerGetEntry(h, RPMTAG_RELEASE, &type,
(void **) &release, &count);
if (rpmfindVerbose > 1)
printf(" %s-%s-%s\n", name, version, release);
found = 0;
break;
}
}
}
dbiFreeIndexRecord(matches);
} else {
res = rpmdbFindByLabel(myDb, resource, &matches);
if (res == 1) {
if (rpmfindVerbose > 1)
printf("Resource %s not installed\n", resource);
} else if (res == 2) {
if (rpmfindVerbose)
printf("Error looking for package %s\n", resource);
} else {
int j;
if (rpmfindVerbose > 1)
printf("Package %s is installed:", resource);
for (j = 0; j < matches.count; j++) {
if (matches.recs[j].recOffset) {
h = rpmdbGetRecord(myDb, matches.recs[j].recOffset);
if (!h) {
fprintf(stderr,
"error: could not read database record\n");
} else {
/* extract informations from the header */
headerGetEntry(h, RPMTAG_NAME, &type,
(void **) &name, &count);
headerGetEntry(h, RPMTAG_VERSION, &type,
(void **) &version, &count);
headerGetEntry(h, RPMTAG_RELEASE, &type,
(void **) &release, &count);
if (rpmfindVerbose > 1)
printf(" %s-%s-%s",
name, version, release);
found = 0;
}
}
}
if (rpmfindVerbose > 1)
printf("\n");
dbiFreeIndexRecord(matches);
}
}
/*
* If the package was found or if we are in the "latest" or
* "upgrade" mode, then do a network lookup.
*/
if ((found < 0) || (rpmfindMode == RPM_FIND_UPGRADE) ||
((rpmfindMode == RPM_FIND_LATEST) && (father == NULL))) {
/*
* Avoid doing request for resources specified by filename
*/
if (resource[0] == '/')
goto return_found;
/*
* Don't allow a network upgrade of glibc.
*/
if ((!strcmp(resource, "glibc")) || (!strncmp(resource, "glibc.so", 8)))
goto return_found;
/*
* Don't allow a network upgrade of libc.
*/
if ((!strcmp(resource, "libc")) || (!strncmp(resource, "libc.so", 7)))
goto return_found;
res = lookupRemoteResource(father, resource);
if (found < 0) goto return_res;
if (res < 0) goto return_found;
/*
* Check that the version found on-line is more recent than
* the installed one.
*/
if ((name == NULL) || (version == NULL) || (release == NULL))
goto return_res;
lookup = neededResourceSearch(resource);
if (lookup == NULL) goto return_found;
for (i = 0;i < lookup->nb_pkgs; i++) {
if (lookup->pkgs[i]->status == STATUS_TO_INSTALL) {
int v1, v2, r1 = 0, r2 = 0;
if (strcmp(lookup->pkgs[i]->name, name)) {
/*
* We use another package to provide this resource
* on this system, inadequate.
*/
if (rpmfindVerbose > 1)
printf("Drop %s, %s provided by %s\n",
lookup->pkgs[i]->name, resource, name);
neededPackageDereference(lookup->pkgs[i]);
continue;
}
v1 = rpmFindVersionValue(lookup->pkgs[i]->version);
v2 = rpmFindVersionValue(version);
sscanf(lookup->pkgs[i]->release, "%d", &r1);
sscanf(release, "%d", &r2);
if ((v2 > v1) || ((v2 == v1) && (r2 >= r1))) {
/*
* The suggested package is older, remove it !
*/
if (rpmfindVerbose > 1)
printf("Drop %s-%s-%s, older than current\n",
lookup->pkgs[i]->name,
lookup->pkgs[i]->version,
lookup->pkgs[i]->release);
neededPackageDereference(lookup->pkgs[i]);
continue;
} else {
if (rpmfindVerbose > 1)
printf("Package %s-%s-%s, candidate for install\n",
lookup->pkgs[i]->name,
lookup->pkgs[i]->version,
lookup->pkgs[i]->release);
goto return_res;
}
}
}
}
return_found:
if (h != NULL)
headerFree(h);
return(found);
return_res:
if (h != NULL)
headerFree(h);
return(res);
}
/*
* try to guess the distribution of the machine
* algorithm : list all the installed packages,
* and keep the most commonly used
* Distribution/Vendor couple ...
*/
#define NB_DIST_ENTRY 10
typedef struct distEntry {
int count; /* How many time was it found */
char *distribution; /* Distribution string */
char *vendor; /* Vendor string */
} distEntry, *distEntryPtr;
#define ENTRY_POP(i) { \
int c; char *d;char *v; \
c = frequent[i - 1].count; \
d = frequent[i - 1].distribution; \
v = frequent[i - 1].vendor; \
frequent[i - 1].count = frequent[i].count; \
frequent[i - 1].vendor = frequent[i].vendor; \
frequent[i - 1].distribution = frequent[i].distribution; \
frequent[i].count = c; \
frequent[i].distribution = d; \
frequent[i].vendor = v; \
}
void guessRpmDistribution(void) {
Header h = NULL;
int offset;
char *distribution;
char *vendor;
int_32 count, type;
distEntry frequent[NB_DIST_ENTRY + 1];
int i;
/* initial cleanup */
for (i = 0;i <= NB_DIST_ENTRY;i++) {
frequent[i].count = 0;
frequent[i].distribution = NULL;
frequent[i].vendor = NULL;
}
nbInstalledPackages = 0;
/* walk the database looking for each installed package */
offset = rpmdbFirstRecNum(myDb);
while (offset) {
h = rpmdbGetRecord(myDb, offset);
if (!h) {
fprintf(stderr, "could not read database record!\n");
return;
}
/* read the distribution field first that's the most selective */
if ((headerGetEntry(h, RPMTAG_DISTRIBUTION, &type,
(void *) &distribution, &count)) &&
(distribution != NULL) && (type == RPM_STRING_TYPE)) {
if ((headerGetEntry(h, RPMTAG_VENDOR, &type,
(void *) &vendor, &count)) &&
(distribution != NULL) && (type == RPM_STRING_TYPE)) {
for (i = 0;i <= NB_DIST_ENTRY;i++) {
if (frequent[i].count == 0) {
/* Ok, add a new entry !*/
frequent[i].count = 1;
frequent[i].distribution = strdup(distribution);
frequent[i].vendor = strdup(vendor);
break;
}
if ((!strcmp(distribution, frequent[i].distribution)) &&
(!strcmp(vendor, frequent[i].vendor))) {
/* Ok, increase count */
frequent[i].count++;
while ((i > 0) &&
(frequent[i].count > frequent[i - 1].count)) {
ENTRY_POP(i)
i--;
}
break;
}
}
if (i > NB_DIST_ENTRY) {
if (frequent[NB_DIST_ENTRY].count != 0) {
free(frequent[NB_DIST_ENTRY].distribution);
free(frequent[NB_DIST_ENTRY].vendor);
}
frequent[NB_DIST_ENTRY].count = 1;
frequent[NB_DIST_ENTRY].distribution = strdup(distribution);
frequent[NB_DIST_ENTRY].vendor = strdup(vendor);
}
}
}
headerFree(h);
nbInstalledPackages++;
offset = rpmdbNextRecNum(myDb, offset);
}
if (frequent[0].count == 0) {
fprintf(stderr, "No installed packages ???\n");
fprintf(stderr, "Using default distribution : %s(%s)\n",
myVendor, myDistribution);
} else {
myVendor = strdup(frequent[0].vendor);
myDistribution = strdup(frequent[0].distribution);
if (rpmfindVerbose)
printf("Default distribution : %s(%s)\n",
myVendor, myDistribution);
if (rpmfindVerbose)
printf("\towning %d of %d installed packages\n",
frequent[0].count, nbInstalledPackages);
}
/* cleanup */
for (i = 0;i <= NB_DIST_ENTRY;i++) {
if (frequent[i].distribution != NULL) free(frequent[i].distribution);
if (frequent[i].vendor != NULL) free(frequent[i].vendor);
}
}
/*
* Initialize the whole setup :
* - Open the RPM database
* - Learn the Architecture/System used
* - Guess the current Vendor/Distribution
*/
void initializeRpmLookup(void) {
myTime = time(NULL);
/*
* initialize.
*/
rpmReadConfigFiles(NULL, NULL, NULL, 0);
/*
* Read the current setup.
*/
rpmGetArchInfo(&myArch, NULL);
rpmGetOsInfo(&myOs, NULL);
if (rpmfindVerbose)
printf("Arch : %s, Os : %s\n", myArch, myOs);
/*
* Open the database.
*/
if (rpmdbOpen(myPrefix, &myDb, O_RDONLY, 0644)) {
fprintf(stderr, "cannot open the RPM local database\n");
exit(1);
}
/*
* try to guess the installed distribution.
*/
guessRpmDistribution();
}
/*
* Cleanup at the end of the operations.
*/
void terminateRpmLookup(void) {
/*
* Close the database.
*/
rpmdbClose(myDb);
}
/*
* Seem that all the dependancies have been resolved, display the current
* suggested list of RPM to fetch.
* In case of user interaction indicatin that the process is not complete
* the function returns a non zero value.
*/
int rpmFindDisplay(void) {
dumpTransferList();
terminateRpmLookup();
return(0);
}
/*
* The main !
*/
int main(int argc, char **argv) {
int i = 1;
int interactive = 0;
int nb_packages = 0;
int res;
char msg[100];
char *catalog = NULL;
/*
* Initialize the network layer.
*/
initializeTransport(handleTransportEnd);
/*
* initialize state from the config files
*/
rpmFindReadConfigFiles();
/*
* Parse the command line args
*/
while (i < argc) {
if (argv[i][0] == '-') {
if (!strcmp(argv[i], "-q")) rpmfindVerbose--;
else if (!strcmp(argv[i], "-i")) interactive++;
else if (!strcmp(argv[i], "-v")) rpmfindVerbose++;
else if (!strcmp(argv[i], "-s")) {
i++;
myServer = argv[i];
} else if (!strcmp(argv[i], "-p")) {
i++;
myPrefix = argv[i];
} else if (!strcmp(argv[i], "--lookup")) {
rpmfindMode = RPM_FIND_LOOKUP;
} else if (!strcmp(argv[i], "--latest")) {
rpmfindMode = RPM_FIND_LATEST;
} else if (!strcmp(argv[i], "--upgrade")) {
rpmfindMode = RPM_FIND_UPGRADE;
} else if (!strcmp(argv[i], "--apropos")) {
rpmfindMode = RPM_FIND_APROPOS;
}
} else
nb_packages++;
i++;
}
if (nb_packages == 0) {
printf("%s %s : RPM packages search engine\n",
RPMFIND_NAME, RPMFIND_VER);
printf("usage : %s [flags] packagename [packagename ...]\n", argv[0]);
printf(" Homepage: %s\n", RPMFIND_URL);
printf(" Maintainer: %s <%s>\n", RPMFIND_MAINT, RPMFIND_MAIL);
printf("\tflags\t\tmeaning\n");
printf("\t-q or -v\tdecrease or increase verbosity\n");
printf("\t-p prefix\tprefix for the local RPM database\n");
printf("\t-s server\tURL of the server to contact\n");
printf("\t--lookup\tsimple lookup (standard mode)\n");
printf("\t--latest\tsuggest upgrades for the package[s]\n");
printf("\t--upgrade\tsuggest upgrades for all dependencies\n");
printf("\t--apropos\tdo a lookup in the database for the keyword\n");
if (configNeedUpdate) rpmFindWriteConfigFile();
exit(0);
}
if (rpmfindMode != RPM_FIND_APROPOS)
initializeRpmLookup();
else {
catalog = loadAproposCatalog();
if (catalog == NULL) exit(1);
}
/*
* Search all the items
*/
i = 1;
while (i < argc) {
if (argv[i][0] == '-') {
if (!strcmp(argv[i], "-s")) {
i++;
} else if (!strcmp(argv[i], "-p")) {
i++;
}
} else if (rpmfindMode == RPM_FIND_APROPOS) {
aproposSearchRdf(argv[i], catalog);
} else {
res = findResource(NULL, argv[i]);
if (res < 0) {
printf("Cannot install or locate resource %s \n", argv[i]);
printf("Do you want to search it in the catalog ? [Y/n] : ");
fflush(stdout);
res = scanf("%s", &msg[0]);
if (res >= 0) {
if (res == 0) msg[0] = 'y';
switch (msg[0]) {
case 'n':
case 'N': break;
case 'y':
case 'Y':
if (catalog == NULL)
catalog = loadAproposCatalog();
if (catalog == NULL) break;
aproposSearchRdf(argv[i], catalog);
break;
default:
break;
}
}
}
else if (res == 0) {
if (rpmfindMode == RPM_FIND_LOOKUP)
printf("Resource %s already installed\n", argv[i]);
else if (rpmfindMode == RPM_FIND_LATEST)
printf("Resource %s latest version already installed\n",
argv[i]);
else
printf("Resource %s : no need to upgrade\n",
argv[i]);
}
else if (res > 0)
printf("Installing %s will require %d KBytes\n",
argv[i], res / 1024);
}
if (interactive) refinePackageSelection(argv[i]);
i++;
}
if (rpmfindMode != RPM_FIND_APROPOS) {
rpmFindDisplay();
rpmFindDownload();
}
if (configNeedUpdate) rpmFindWriteConfigFile();
/*
* Cleanup the cache.
*/
netResourceClean();
exit(0);
}
Webmaster