1
0
mirror of https://github.com/openSUSE/libsolv.git synced 2026-02-05 12:45:46 +01:00

- add tiny demo installer program "solv"

- add transaction_installedresult function
- fix bug in conflict detection
This commit is contained in:
Michael Schroeder
2009-06-23 15:32:06 +02:00
parent 569a6ffb60
commit b986aaf83c
2 changed files with 727 additions and 0 deletions

2
examples/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
ADD_EXECUTABLE(solv solv.c)
TARGET_LINK_LIBRARIES(solv satsolverext satsolver ${RPMDB_LIBRARY} ${EXPAT_LIBRARY} ${ZLIB_LIBRARY})

725
examples/solv.c Normal file
View File

@@ -0,0 +1,725 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <zlib.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "pool.h"
#include "poolarch.h"
#include "repo.h"
#include "util.h"
#include "solver.h"
#include "solverdebug.h"
#include "repo_rpmdb.h"
#include "repo_rpmmd.h"
#include "repo_susetags.h"
#include "repo_repomdxml.h"
#include "pool_fileconflicts.h"
/* solv, a little demo application */
struct repoinfo {
Repo *repo;
char *alias;
char *name;
int enabled;
int autorefresh;
char *baseurl;
char *path;
int type;
int priority;
int keeppackages;
};
#define TYPE_UNKNOWN 0
#define TYPE_SUSETAGS 1
#define TYPE_RPMMD 2
struct repoinfo *
read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
{
char buf[4096];
char buf2[4096], *kp, *vp, *kpe;
DIR *dir;
FILE *fp;
struct dirent *ent;
int l, rdlen;
struct repoinfo *repoinfos = 0, *cinfo;
int nrepoinfos = 0;
rdlen = strlen(reposdir);
dir = opendir(reposdir);
if (!dir)
{
*nrepoinfosp = 0;
return 0;
}
while ((ent = readdir(dir)) != 0)
{
l = strlen(ent->d_name);
if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
continue;
snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
if ((fp = fopen(buf, "r")) == 0)
{
perror(buf);
continue;
}
cinfo = 0;
while(fgets(buf2, sizeof(buf2), fp))
{
l = strlen(buf2);
if (l == 0)
continue;
while (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t')
buf2[--l] = 0;
kp = buf2;
while (*kp == ' ' || *kp == '\t')
kp++;
if (!*kp || *kp == '#')
continue;
if (!cinfo)
{
if (*kp != '[')
continue;
vp = strrchr(kp, ']');
if (!vp)
continue;
*vp = 0;
repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
cinfo = repoinfos + nrepoinfos++;
memset(cinfo, 0, sizeof(*cinfo));
cinfo->alias = strdup(kp + 1);
continue;
}
vp = strchr(kp, '=');
if (!vp)
continue;
for (kpe = vp - 1; kpe >= kp; kpe--)
if (*kpe != ' ' && *kpe != '\t')
break;
if (kpe == kp)
continue;
vp++;
while (*vp == ' ' || *vp == '\t')
vp++;
kpe[1] = 0;
if (!strcmp(kp, "name"))
cinfo->name = strdup(vp);
else if (!strcmp(kp, "enabled"))
cinfo->enabled = *vp == '0' ? 0 : 1;
else if (!strcmp(kp, "autorefresh"))
cinfo->autorefresh = *vp == '0' ? 0 : 1;
else if (!strcmp(kp, "baseurl"))
cinfo->baseurl = strdup(vp);
else if (!strcmp(kp, "path"))
cinfo->path = strdup(vp);
else if (!strcmp(kp, "type"))
{
if (!strcmp(vp, "yast2"))
cinfo->type = TYPE_SUSETAGS;
else if (!strcmp(vp, "rpm-md"))
cinfo->type = TYPE_RPMMD;
else
cinfo->type = TYPE_UNKNOWN;
}
else if (!strcmp(kp, "priority"))
cinfo->priority = atoi(vp);
else if (!strcmp(kp, "keeppackages"))
cinfo->keeppackages = *vp == '0' ? 0 : 1;
}
fclose(fp);
if (cinfo->type == TYPE_SUSETAGS && cinfo->baseurl)
{
char *old = cinfo->baseurl;
cinfo->baseurl = sat_malloc(5 + strlen(old) + 1);
sprintf(cinfo->baseurl, "%s/suse", old);
free(old);
}
cinfo = 0;
}
closedir(dir);
*nrepoinfosp = nrepoinfos;
return repoinfos;
}
static ssize_t
cookie_gzread(void *cookie, char *buf, size_t nbytes)
{
return gzread((gzFile *)cookie, buf, nbytes);
}
static int
cookie_gzclose(void *cookie)
{
return gzclose((gzFile *)cookie);
}
FILE *
myfopen(const char *fn)
{
cookie_io_functions_t cio;
char *suf;
gzFile *gzf;
if (!fn)
return 0;
suf = strrchr(fn, '.');
if (!suf || strcmp(suf, ".gz") != 0)
return fopen(fn, "r");
gzf = gzopen(fn, "r");
if (!gzf)
return 0;
memset(&cio, 0, sizeof(cio));
cio.read = cookie_gzread;
cio.close = cookie_gzclose;
return fopencookie(gzf, "r", cio);
}
FILE *
curlfopen(char *baseurl, char *file, int uncompress)
{
pid_t pid;
int fd, l;
int status;
char tmpl[100];
char url[4096];
l = strlen(baseurl);
if (l && baseurl[l - 1] == '/')
snprintf(url, sizeof(url), "%s%s", baseurl, file);
else
snprintf(url, sizeof(url), "%s/%s", baseurl, file);
strcpy(tmpl, "/var/tmp/solvXXXXXX");
fd = mkstemp(tmpl);
if (fd < 0)
{
perror("mkstemp");
exit(1);
}
unlink(tmpl);
if ((pid = fork()) == (pid_t)-1)
{
perror("fork");
exit(1);
}
if (pid == 0)
{
if (fd != 1)
{
dup2(fd, 1);
close(fd);
}
execlp("curl", "curl", "-s", "-L", url, (char *)0);
perror("curl");
_exit(0);
}
while (waitpid(pid, &status, 0) != pid)
;
lseek(fd, 0, SEEK_SET);
if (uncompress)
{
cookie_io_functions_t cio;
gzFile *gzf;
sprintf(tmpl, "/dev/fd/%d", fd);
gzf = gzopen(tmpl, "r");
close(fd);
if (!gzf)
return 0;
memset(&cio, 0, sizeof(cio));
cio.read = cookie_gzread;
cio.close = cookie_gzclose;
return fopencookie(gzf, "r", cio);
}
return fdopen(fd, "r");
}
void
setarch(Pool *pool)
{
struct utsname un;
if (uname(&un))
{
perror("uname");
exit(1);
}
pool_setarch(pool, un.machine);
}
void
read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
{
Repo *repo;
struct repoinfo *cinfo;
int i;
FILE *fp;
printf("reading rpm database\n");
repo = repo_create(pool, "@System");
repo_add_rpmdb(repo, 0, 0, 0);
pool_set_installed(pool, repo);
for (i = 0; i < nrepoinfos; i++)
{
cinfo = repoinfos + i;
if (!cinfo->enabled)
continue;
switch (cinfo->type)
{
case TYPE_RPMMD:
printf("reading rpmmd repo '%s'\n", cinfo->alias);
if ((fp = curlfopen(cinfo->baseurl, "repodata/repomd.xml", 0)) == 0)
break;
repo = repo_create(pool, cinfo->alias);
cinfo->repo = repo;
repo_add_repomdxml(repo, fp, 0);
fclose(fp);
if ((fp = curlfopen(cinfo->baseurl, "repodata/primary.xml.gz", 1)) == 0)
continue;
repo_add_rpmmd(repo, fp, 0, 0);
fclose(fp);
break;
case TYPE_SUSETAGS:
printf("reading susetags repo '%s'\n", cinfo->alias);
if ((fp = curlfopen(cinfo->baseurl, "setup/descr/packages.gz", 1)) == 0)
break;
repo = repo_create(pool, cinfo->alias);
cinfo->repo = repo;
repo_add_susetags(repo, fp, 0, 0, 0);
fclose(fp);
break;
default:
printf("skipping unknown repo '%s'\n", cinfo->alias);
break;
}
}
}
void
mkselect(Pool *pool, const char *arg, int flags, Queue *out)
{
Id id, p, pp;
Id type = 0;
const char *r, *r2;
id = str2id(pool, arg, 0);
if (id)
{
FOR_PROVIDES(p, pp, id)
{
Solvable *s = pool_id2solvable(pool, p);
if (s->name == id)
{
type = SOLVER_SOLVABLE_NAME;
break;
}
type = SOLVER_SOLVABLE_PROVIDES;
}
}
if (!type)
{
/* did not find a solvable, see if it's a relation */
if ((r = strpbrk(arg, "<=>")) != 0)
{
Id rid, rname, revr;
int rflags = 0;
for (r2 = r; r2 > arg && (r2[-1] == ' ' || r2[-1] == '\t'); )
r2--;
rname = r2 > arg ? strn2id(pool, arg, r2 - arg, 1) : 0;
for (; *r; r++)
{
if (*r == '<')
rflags |= REL_LT;
else if (*r == '=')
rflags |= REL_EQ;
else if (*r == '>')
rflags |= REL_GT;
else
break;
}
while (*r == ' ' || *r == '\t')
r++;
revr = *r ? str2id(pool, r, 1) : 0;
rid = rname && revr ? rel2id(pool, rname, revr, rflags, 1) : 0;
if (rid)
{
FOR_PROVIDES(p, pp, rid)
{
Solvable *s = pool_id2solvable(pool, p);
if (pool_match_nevr(pool, s, rid))
{
type = SOLVER_SOLVABLE_NAME;
break;
}
type = SOLVER_SOLVABLE_PROVIDES;
}
}
if (type)
id = rid;
}
}
if (type)
{
queue_push(out, type);
queue_push(out, id);
}
}
int
yesno(const char *str)
{
char inbuf[128], *ip;
for (;;)
{
printf("%s", str);
fflush(stdout);
*inbuf = 0;
if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
{
printf("Abort.\n");
exit(1);
}
while (*ip == ' ' || *ip == '\t')
ip++;
if (*ip == 'q')
{
printf("Abort.\n");
exit(1);
}
if (*ip == 'y' || *ip == 'n')
return *ip == 'y' ? 1 : 0;
}
}
struct fcstate {
FILE **newpkgsfps;
Id *newpkgsps;
int newpkgscnt;
void *rpmdbstate;
};
static void *
fc_cb(Pool *pool, Id p, void *cbdata)
{
struct fcstate *fcstate = cbdata;
Solvable *s;
Id rpmdbid;
int i;
FILE *fp;
if (!p)
{
rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
return 0;
}
s = pool_id2solvable(pool, p);
if (pool->installed && s->repo == pool->installed)
{
if (!s->repo->rpmdbid)
return 0;
rpmdbid = s->repo->rpmdbid[p - s->repo->start];
if (!rpmdbid)
return 0;
return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
}
for (i = 0; i < fcstate->newpkgscnt; i++)
if (fcstate->newpkgsps[i] == p)
break;
if (i == fcstate->newpkgscnt)
return 0;
fp = fcstate->newpkgsfps[i];
rewind(fp);
return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
}
void
runrpm(const char *arg, const char *name)
{
pid_t pid;
int status;
if ((pid = fork()) == (pid_t)-1)
{
perror("fork");
exit(1);
}
if (pid == 0)
{
if (strcmp(arg, "-e") == 0)
execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
else
execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
perror("rpm");
_exit(0);
}
while (waitpid(pid, &status, 0) != pid)
;
if (status)
{
printf("rpm failed\n");
exit(1);
}
}
int
main(int argc, char **argv)
{
Pool *pool;
Id p, pp;
struct repoinfo *repoinfos;
int nrepoinfos = 0;
int i, mode, newpkgs;
Queue job, checkq;
Solver *solv = 0;
Transaction *trans;
char inbuf[128], *ip;
int updateall = 0;
FILE **newpkgsfps = 0;
Id *newpkgsps = 0;
struct fcstate fcstate;
pool = pool_create();
// pool_setdebuglevel(pool, 2);
setarch(pool);
repoinfos = read_repoinfos(pool, "/etc/zypp/repos.d", &nrepoinfos);
read_repos(pool, repoinfos, nrepoinfos);
// FOR_REPOS(i, repo)
// printf("%s: %d solvables\n", repo->name, repo->nsolvables);
pool_addfileprovides(pool);
pool_createwhatprovides(pool);
if (!strcmp(argv[1], "install") || !strcmp(argv[1], "in"))
mode = SOLVER_INSTALL;
else if (!strcmp(argv[1], "erase") || !strcmp(argv[1], "rm"))
mode = SOLVER_ERASE;
else if (!strcmp(argv[1], "show"))
mode = 0;
else if (!strcmp(argv[1], "update") || !strcmp(argv[1], "up"))
mode = SOLVER_UPDATE;
else
{
fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
exit(1);
}
queue_init(&job);
for (i = 2; i < argc; i++)
mkselect(pool, argv[i], 0, &job);
if (!job.count && mode == SOLVER_UPDATE)
updateall = 1;
else if (!job.count)
{
printf("nothing matched\n");
exit(1);
}
if (!mode)
{
for (i = 0; i < job.count; i += 2)
{
FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
printf(" - %s\n", solvid2str(pool, p));
}
exit(0);
}
// add mode
for (i = 0; i < job.count; i += 2)
job.elements[i] |= mode;
rerunsolver:
for (;;)
{
Id problem, solution;
int pcnt, scnt;
solv = solver_create(pool);
solv->ignorealreadyrecommended = 1;
solv->updatesystem = updateall;
solver_solve(solv, &job);
if (!solv->problems.count)
break;
pcnt = solver_problem_count(solv);
printf("Found %d problems:\n", pcnt);
for (problem = 1; problem <= pcnt; problem++)
{
int take = 0;
printf("Problem %d:\n", problem);
solver_printprobleminfo(solv, problem);
printf("\n");
scnt = solver_solution_count(solv, problem);
for (solution = 1; solution <= pcnt; solution++)
{
printf("Solution %d:\n", solution);
solver_printsolution(solv, problem, solution);
printf("\n");
}
for (;;)
{
printf("Please choose a solution: ");
fflush(stdout);
*inbuf = 0;
if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
{
printf("Abort.\n");
exit(1);
}
while (*ip == ' ' || *ip == '\t')
ip++;
if (*ip >= '0' && *ip <= '9')
{
take = atoi(ip);
if (take >= 1 && take <= solution)
break;
}
if (*ip == 's')
{
take = 0;
break;
}
if (*ip == 'q')
{
printf("Abort.\n");
exit(1);
}
}
if (!take)
continue;
solver_take_solution(solv, problem, take, &job);
}
solver_free(solv);
solv = 0;
}
if (!solv->trans.steps.count)
{
printf("Nothing to do.\n");
exit(1);
}
printf("\n");
printf("Transaction summary:\n\n");
solver_printtransaction(solv);
if (!yesno("OK to continue (y/n)? "))
{
printf("Abort.\n");
exit(1);
}
trans = &solv->trans;
queue_init(&checkq);
newpkgs = transaction_installedresult(trans, &checkq);
if (newpkgs)
{
Queue conflicts;
printf("Downloading %d packages\n", newpkgs);
newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
newpkgsps = sat_calloc(newpkgs, sizeof(Id));
for (i = 0; i < newpkgs; i++)
{
int j;
unsigned int medianr;
char *loc;
Solvable *s;
struct repoinfo *cinfo;
p = checkq.elements[i];
newpkgsps[i] = p;
s = pool_id2solvable(pool, p);
loc = solvable_get_location(s, &medianr);
cinfo = 0;
for (j = 0; j < nrepoinfos; j++)
if (s->repo == repoinfos[j].repo)
{
cinfo = repoinfos + j;
break;
}
if ((newpkgsfps[i] = curlfopen(cinfo->baseurl, loc, 0)) == 0)
{
printf("%s: %s not found\n", cinfo->alias, loc);
exit(1);
}
}
printf("Searching for file conflicts\n");
queue_init(&conflicts);
fcstate.rpmdbstate = 0;
fcstate.newpkgscnt = newpkgs;
fcstate.newpkgsfps = newpkgsfps;
fcstate.newpkgsps = newpkgsps;
pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
if (conflicts.count)
{
for (i = 0; i < newpkgs; i++)
fclose(newpkgsfps[i]);
sat_free(newpkgsfps);
sat_free(newpkgsps);
solver_free(solv);
printf("\n");
for (i = 0; i < conflicts.count; i += 5)
printf("file %s of package %s conflicts with package %s\n", id2str(pool, conflicts.elements[i]), solvid2str(pool, conflicts.elements[i + 1]), solvid2str(pool, conflicts.elements[i + 3]));
printf("\n");
if (!yesno("Re-run solver (y/n)? "))
{
printf("Abort.\n");
exit(1);
}
pool_add_fileconflicts_deps(pool, &conflicts);
pool_createwhatprovides(pool);
goto rerunsolver;
}
queue_free(&conflicts);
}
transaction_order(trans, 0);
printf("Committing transaction:\n");
for (i = 0; i < trans->steps.count; i++)
{
char rpmname[256];
const char *evr, *evrp;
Solvable *s;
int j;
FILE *fp;
p = trans->steps.elements[i];
s = pool_id2solvable(pool, p);
Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
switch(type)
{
case SOLVER_TRANSACTION_ERASE:
printf("erase %s\n", solvid2str(pool, p));
if (strlen(solvid2str(pool, p)) > 255)
continue;
evr = evrp = id2str(pool, s->evr);
while (*evrp >= '0' && *evrp <= '9')
evrp++;
if (evrp > evr && evrp[0] == ':' && evrp[1])
evr = evrp + 1;
sprintf(rpmname, "%s-%s.%s", id2str(pool, s->name), evr, id2str(pool, s->arch));
runrpm("-e", rpmname);
break;
case SOLVER_TRANSACTION_INSTALL:
case SOLVER_TRANSACTION_MULTIINSTALL:
printf("install %s\n", solvid2str(pool, p));
for (j = 0; j < newpkgs; j++)
if (newpkgsps[j] == p)
break;
fp = j < newpkgs ? newpkgsfps[j] : 0;
rewind(fp);
lseek(fileno(fp), 0, SEEK_SET);
sprintf(rpmname, "/dev/fd/%d", fileno(fp));
runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", rpmname);
break;
default:
break;
}
}
for (i = 0; i < newpkgs; i++)
fclose(newpkgsfps[i]);
sat_free(newpkgsfps);
sat_free(newpkgsps);
solver_free(solv);
pool_free(pool);
exit(0);
}