refactor individual checks into subfunctions
[platform/upstream/libsolv.git] / tools / patchcheck.c
1 /* vim: sw=2 et
2  */
3
4 /*
5  * Copyright (c) 2009, Novell Inc.
6  *
7  * This program is licensed under the BSD license, read LICENSE.BSD
8  * for further information
9  */
10
11 #define _GNU_SOURCE
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <zlib.h>
18
19 #include "pool.h"
20 #include "evr.h"
21 #include "poolarch.h"
22 #include "repo_solv.h"
23 #include "repo_susetags.h"
24 #include "repo_updateinfoxml.h"
25 #include "repo_rpmmd.h"
26 #include "solver.h"
27 #include "solverdebug.h"
28
29 static ssize_t
30 cookie_gzread(void *cookie, char *buf, size_t nbytes)
31 {
32   return gzread((gzFile *)cookie, buf, nbytes);
33 }
34
35 static int
36 cookie_gzclose(void *cookie)
37 {
38   return gzclose((gzFile *)cookie);
39 }
40
41 FILE *
42 myfopen(const char *fn)
43 {
44   cookie_io_functions_t cio;
45   char *suf;
46   gzFile *gzf;
47
48   if (!fn)
49     return 0;
50   suf = strrchr(fn, '.');
51   if (!suf || strcmp(suf, ".gz") != 0)
52     return fopen(fn, "r");
53   gzf = gzopen(fn, "r");
54   if (!gzf)
55     return 0;
56   memset(&cio, 0, sizeof(cio));
57   cio.read = cookie_gzread;
58   cio.close = cookie_gzclose;
59   return  fopencookie(gzf, "r", cio);
60 }
61
62 void
63 showproblems(Solver *solv, Solvable *s, Queue *cand, Queue *badguys)
64 {
65   Pool *pool = solv->pool;
66   Queue rids, rinfo;
67   Id problem = 0;
68   int jj;
69   int rerun = 0;
70
71   queue_init(&rids);
72   queue_init(&rinfo);
73   printf("can't install %s:\n", solvable2str(pool, s));
74   while ((problem = solver_next_problem(solv, problem)) != 0)
75     {
76       solver_findallproblemrules(solv, problem, &rids);
77       for (jj = 0; jj < rids.count; jj++)
78         {
79           Id probr = rids.elements[jj];
80           int k, l;
81
82           queue_empty(&rinfo);
83           solver_allruleinfos(solv, probr, &rinfo);
84           for (k = 0; k < rinfo.count; k += 4)
85             {
86               Id dep, source, target;
87               source = rinfo.elements[k + 1];
88               target = rinfo.elements[k + 2];
89               dep = rinfo.elements[k + 3];
90               switch (rinfo.elements[k])
91                 {
92                 case SOLVER_PROBLEM_DISTUPGRADE_RULE:
93                   break;
94                 case SOLVER_PROBLEM_INFARCH_RULE:
95                   printf("  %s has inferior architecture\n", solvid2str(pool, source));
96                   break;
97                 case SOLVER_PROBLEM_UPDATE_RULE:
98                   printf("  update rule for %s\n", solvid2str(pool, source));
99                   if (badguys)
100                     queue_pushunique(badguys, source);
101                   if (!cand)
102                     break;
103                   /* only drop update problem packages from cand so that we see all problems of this patch */
104                   for (l = 0; l < cand->count; l++)
105                     if (cand->elements[l] == source || cand->elements[l] == -source)
106                       break;
107                   if (l == cand->count)
108                     break;
109                   if (!rerun)
110                     {
111                       for (l = 0; l < cand->count; l++)
112                         if (cand->elements[l] < 0)
113                           cand->elements[l] = -cand->elements[l];
114                       rerun = 1;
115                     }
116                   for (l = 0; l < cand->count; l++)
117                     if (cand->elements[l] == source)
118                       {
119                         cand->elements[l] = -source;
120                       }
121                   break;
122                 case SOLVER_PROBLEM_JOB_RULE:
123                   break;
124                 case SOLVER_PROBLEM_RPM_RULE:
125                   printf("  some dependency problem\n");
126                   break;
127                 case SOLVER_PROBLEM_JOB_NOTHING_PROVIDES_DEP:
128                   printf("  nothing provides requested %s\n", dep2str(pool, dep));
129                   break;
130                 case SOLVER_PROBLEM_NOT_INSTALLABLE:
131                   printf("  package %s is not installable\n", solvid2str(pool, source));
132                   break;
133                 case SOLVER_PROBLEM_NOTHING_PROVIDES_DEP:
134                   printf("  nothing provides %s needed by %s\n", dep2str(pool, dep), solvid2str(pool, source));
135                   if (ISRELDEP(dep))
136                     {
137                       Reldep *rd = GETRELDEP(pool, dep);
138                       if (!ISRELDEP(rd->name))
139                         {
140                           Id rp, rpp;
141                           FOR_PROVIDES(rp, rpp, rd->name)
142                             printf("    (we have %s)\n", solvid2str(pool, rp));
143                         }
144                     }
145                   break;
146                 case SOLVER_PROBLEM_SAME_NAME:
147                   printf("  cannot install both %s and %s\n", solvid2str(pool, source), solvid2str(pool, target));
148                   break;
149                 case SOLVER_PROBLEM_PACKAGE_CONFLICT:
150                   printf("  package %s conflicts with %s provided by %s\n", solvid2str(pool, source), dep2str(pool, dep), solvid2str(pool, target));
151                   break;
152                 case SOLVER_PROBLEM_PACKAGE_OBSOLETES:
153                   printf("  package %s obsoletes %s provided by %s\n", solvid2str(pool, source), dep2str(pool, dep), solvid2str(pool, target));
154                   break;
155                 case SOLVER_PROBLEM_DEP_PROVIDERS_NOT_INSTALLABLE:
156                   printf("  package %s requires %s, but none of the providers can be installed\n", solvid2str(pool, source), dep2str(pool, dep));
157                   break;
158                 case SOLVER_PROBLEM_SELF_CONFLICT:
159                   printf("  package %s conflicts with %s provided by itself\n", solvid2str(pool, source), dep2str(pool, dep));
160                   break;
161                 }
162             }
163         }
164     }
165   queue_free(&rids);
166   queue_free(&rinfo);
167 }
168
169 void
170 toinst(Solver *solv, Repo *repo, Repo *instrepo)
171 {
172   Pool *pool = solv->pool;
173   int k;
174   Id p;
175
176   for (k = 0; k < solv->decisionq.count; k++)
177     {
178       p = solv->decisionq.elements[k];
179       if (p < 0 || p == SYSTEMSOLVABLE)
180         continue;
181
182      /* printf(" toinstall %s\n", solvid2str(pool, p));*/
183       /* oh my! */
184       pool->solvables[p].repo = instrepo;
185     }
186 }
187
188 void
189 dump_instrepo(Repo *instrepo, Pool *pool)
190 {
191   Solvable *s;
192   Id p;
193
194   printf("instrepo..\n");
195   FOR_REPO_SOLVABLES(instrepo, p, s)
196     printf("  %s\n", solvable2str(pool, s));
197   printf("done.\n");
198 }
199
200 void
201 frominst(Solver *solv, Repo *repo, Repo *instrepo)
202 {
203   Pool *pool = solv->pool;
204   int k;
205
206   for (k = 1; k < pool->nsolvables; k++)
207     if (pool->solvables[k].repo == instrepo)
208       pool->solvables[k].repo = repo;
209 }
210
211 void
212 usage(char** argv)
213 {
214
215   printf("%s: <arch> <patchnameprefix>  [--install-available] [repos] [--updaterepos] [repos]...\n"
216       "\t --install-available: installation repository is available during update\n"
217       "\t repos: repository ending in\n"
218       "\t\tpackages, packages.gz, primary.xml.gz, updateinfo.xml.gz or .solv\n",
219       argv[0]);
220
221   exit(1);
222 }
223
224 typedef struct {
225   int updatestart;
226   int shown;
227   int status;
228   int install_available;
229   Repo *repo;
230   Repo *instrepo;
231 } context_t;
232
233 #define SHOW_PATCH(c) if (!(c)->shown++) printf("%s:\n", solvable2str(pool, s));
234 #define PERF_DEBUGGING 0
235  
236 static Pool *pool;
237
238 void
239 test_all_old_patches_included(context_t *c, Id pid)
240 {
241   Id p, pp;
242   Id con, *conp;
243   Solvable *s = pool->solvables + pid;
244   /* Test 1: are all old patches included */
245   FOR_PROVIDES(p, pp, s->name)
246     {
247       Solvable *s2 = pool->solvables + p;
248       Id con2, *conp2;
249
250       if (!s2->conflicts)
251         continue;
252       if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) <= 0)
253         continue;
254       conp2 = s2->repo->idarraydata + s2->conflicts;
255       while ((con2 = *conp2++) != 0)
256         {
257           Reldep *rd2, *rd;
258           if (!ISRELDEP(con2))
259             continue;
260           rd2 = GETRELDEP(pool, con2);
261           conp = s->repo->idarraydata + s->conflicts;
262           while ((con = *conp++) != 0)
263             {
264               if (!ISRELDEP(con))
265                 continue;
266               rd = GETRELDEP(pool, con);
267               if (rd->name == rd2->name)
268                 break;
269             }
270           if (!con)
271             {
272               SHOW_PATCH(c);
273               printf("  %s contained %s\n", solvable2str(pool, s2), dep2str(pool, rd2->name));
274             }
275           else
276            {
277              if (evrcmp(pool, rd->evr, rd2->evr, EVRCMP_COMPARE) < 0)
278                {
279                  SHOW_PATCH(c);
280                  printf("  %s required newer version %s-%s of %s-%s\n",
281                      solvable2str(pool, s2), dep2str(pool, rd2->name), dep2str(pool, rd2->evr),
282                      dep2str(pool, rd->name), dep2str(pool, rd->evr));
283                }
284            }
285     
286         }
287     }
288 }
289
290 void
291 test_all_packages_installable(context_t *c, Id pid)
292 {
293   Solver *solv;
294   Queue job;
295   Id p, pp;
296   Id con, *conp;
297   unsigned int now, solver_runs;
298   int i;
299   Solvable *s = pool->solvables + pid;
300
301   queue_init(&job);
302
303   now = sat_timems(0);
304   solver_runs = 0;
305
306   conp = s->repo->idarraydata + s->conflicts;
307   while ((con = *conp++) != 0)
308     {
309       FOR_PROVIDES(p, pp, con)
310         {
311           queue_empty(&job);
312           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
313           queue_push(&job, p);
314
315           /* also set up some minimal system */
316           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
317           queue_push(&job, str2id(pool, "rpm", 1));
318           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
319           queue_push(&job, str2id(pool, "aaa_base", 1));
320
321           solv = solver_create(pool);
322           solv->dontinstallrecommended = 0;
323           ++solver_runs;
324           solver_solve(solv, &job);
325           if (solv->problems.count)
326             {
327               c->status = 1;
328               printf("error installing original package\n");
329               showproblems(solv, s, 0, 0);
330             }
331           toinst(solv, c->repo, c->instrepo);
332           solver_free(solv);
333
334 #if 0
335           dump_instrepo(instrepo, pool);
336
337 #endif
338           if (!c->install_available)
339             {
340               queue_empty(&job);
341               for (i = 1; i < c->updatestart; i++)
342                 {
343                   if (pool->solvables[i].repo != c->repo || i == pid)
344                     continue;
345                   queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
346                   queue_push(&job, i);
347                 }
348               queue_push(&job, SOLVER_INSTALL_SOLVABLE);
349               queue_push(&job, pid);
350               solv = solver_create(pool);
351               /*solv->dontinstallrecommended = 1;*/
352               ++solver_runs;
353               solver_solve(solv, &job);
354               if (solv->problems.count)
355                 {
356                   c->status = 1;
357                   showproblems(solv, s, 0, 0);
358                 }
359               frominst(solv, c->repo, c->instrepo);
360               solver_free(solv);
361             }
362         }
363     }
364
365   if (PERF_DEBUGGING)
366     printf("  test_all_packages_installable took %d ms in %d runs\n", sat_timems(now), solver_runs);
367 }
368
369 void
370 test_can_upgrade_all_packages(context_t *c, Id pid)
371 {
372   Solver *solv;
373   Id p;
374   Id con, *conp;
375   Queue job;
376   Queue cand;
377   Queue badguys;
378   int i, j;
379   Solvable *s = pool->solvables + pid;
380
381   queue_init(&job);
382   queue_init(&cand);
383   queue_init(&badguys);
384
385   /* Test 3: can we upgrade all packages? */
386   for (p = 1; p < pool->nsolvables; p++)
387     {
388       Solvable *s = pool->solvables + p;
389       if (!s->repo)
390         continue;
391       if (strchr(id2str(pool, s->name), ':'))
392         continue;       /* only packages, please */
393       if (!pool_installable(pool, s))
394         continue;
395       queue_push(&cand, p);
396     }
397   while (cand.count)
398     {
399       solv = solver_create(pool);
400       queue_empty(&job);
401       for (i = 0; i < badguys.count; i++)
402         {
403           queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE|SOLVER_WEAK);
404           queue_push(&job, badguys.elements[i]);
405         }
406       conp = s->repo->idarraydata + s->conflicts;
407       while ((con = *conp++) != 0)
408         {
409           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
410           queue_push(&job, con);
411         }
412       for (i = 0; i < cand.count; i++)
413         {
414           p = cand.elements[i];
415           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
416           queue_push(&job, p);
417         }
418       solver_solve(solv, &job);
419 #if 0
420       solver_printdecisions(solv);
421 #endif
422       /* put packages into installed repo and prune them from cand */
423       toinst(solv, c->repo, c->instrepo);
424       for (i = 0; i < cand.count; i++)
425         {
426           p = cand.elements[i];
427           if (p > 0 && solv->decisionmap[p] > 0)
428             cand.elements[i] = -p;      /* drop candidate */
429         }
430       solver_free(solv);
431
432       /* now the interesting part: test patch */
433       queue_empty(&job);
434 #if 0
435       for (i = 1; i < updatestart; i++)
436         {
437           if (pool->solvables[i].repo != repo || i == pid)
438             continue;
439           queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
440           queue_push(&job, i);
441         }
442 #endif
443       queue_push(&job, SOLVER_INSTALL_SOLVABLE);
444       queue_push(&job, pid);
445       solv = solver_create(pool);
446       solv->dontinstallrecommended = 1;
447       solver_solve(solv, &job);
448
449       if (solv->problems.count)
450         {
451           c->status = 1;
452           showproblems(solv, s, &cand, &badguys);
453         }
454       frominst(solv, c->repo, c->instrepo);
455       solver_free(solv);
456       /* now drop all negative elements from cand */
457       for (i = j = 0; i < cand.count; i++)
458         {
459           if (cand.elements[i] < 0)
460             continue;
461           cand.elements[j++] = cand.elements[i];
462         }
463       if (i == j)
464         break;  /* no progress */
465       cand.count = j;
466     }
467 }
468
469 void
470 test_no_ga_package_fulfills_dependency(context_t *c, Id pid)
471 {
472   Id con, *conp;
473   Solvable *s = pool->solvables + pid;
474
475   /* Test 4: no GA package fulfills patch dependency */
476   conp = s->repo->idarraydata + s->conflicts;
477   while ((con = *conp++) != 0)
478     {
479       Reldep *rd;
480       Id rp, rpp;
481
482       if (!ISRELDEP(con))
483         continue;
484       rd = GETRELDEP(pool, con);
485       FOR_PROVIDES(rp, rpp, rd->name)
486         {
487           Solvable *s2 = pool_id2solvable(pool, rp);
488           if (rp < c->updatestart
489               && evrcmp(pool, rd->evr, s2->evr, EVRCMP_COMPARE) < 0
490               && pool_match_nevr_rel(pool, s2, rd->name)
491              )
492             {
493               SHOW_PATCH(c);
494               printf("  conflict %s < %s satisfied by non-updated package %s\n",
495                   dep2str(pool, rd->name), dep2str(pool, rd->evr), solvable2str(pool, s2));
496               break;
497             }
498         }
499     }
500 }
501
502 int
503 main(int argc, char **argv)
504 {
505   char *arch, *mypatch;
506   const char *pname;
507   int l;
508   FILE *fp;
509   int i;
510   Id pid, p, pp;
511   int tests = 0;
512   context_t c;
513
514   c.install_available = 0;
515   c.updatestart = 0;
516   c.status = 0;
517
518   if (argc <= 3)
519     usage(argv);
520
521   arch = argv[1];
522   pool = pool_create();
523   pool_setarch(pool, arch);
524   static const char* langs[] = {"en"};
525   pool_set_languages(pool, langs, 1);
526
527 #if 0
528   pool_setdebuglevel(pool, 2);
529 #endif
530
531   mypatch = argv[2];
532
533   c.repo = repo_create(pool, 0);
534   c.instrepo = repo_create(pool, 0);
535   for (i = 3; i < argc; i++)
536     {
537       if (!strcmp(argv[i], "--updaterepos"))
538         {
539           c.updatestart = pool->nsolvables;
540           continue;
541         }
542
543       if (!strcmp(argv[i], "--install-available"))
544         {
545           c.install_available = 1;
546           continue;
547         }
548  
549       l = strlen(argv[i]);
550       if (!strcmp(argv[i], "-"))
551         fp = stdin;
552       else if ((fp = myfopen(argv[i])) == 0)
553         {
554           perror(argv[i]);
555           exit(1);
556         }
557       if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
558         {
559           repo_add_susetags(c.repo, fp, 0, 0, 0);
560         }
561       else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
562         {
563           repo_add_susetags(c.repo, fp, 0, 0, 0);
564         }
565       else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
566         {
567           repo_add_rpmmd(c.repo, fp, 0, 0);
568         }
569       else if (l >= 17 && !strcmp(argv[i] + l - 17, "updateinfo.xml.gz"))
570         {
571           repo_add_updateinfoxml(c.repo, fp, 0);
572         }
573       else if (repo_add_solv(c.repo, fp))
574         {
575           fprintf(stderr, "could not add repo %s\n", argv[i]);
576           exit(1);
577         }
578       if (fp != stdin)
579         fclose(fp);
580     }
581
582   pool_addfileprovides(pool);
583
584   /* bad hack ahead: clone repo */
585   c.instrepo->idarraydata = c.repo->idarraydata;
586   c.instrepo->idarraysize = c.repo->idarraysize;
587   c.instrepo->start = c.repo->start;
588   c.instrepo->end = c.repo->end;
589   c.instrepo->nsolvables = c.repo->nsolvables;  /* sic! */
590   c.instrepo->lastoff = c.repo->lastoff;        /* sic! */
591   pool_set_installed(pool, c.instrepo);
592   pool_createwhatprovides(pool);
593
594   for (pid = 1; pid < pool->nsolvables; pid++)
595     {
596       c.shown = 0;
597       Solvable *s = pool->solvables + pid;
598       if (!s->repo)
599         continue;
600       if (!pool_installable(pool, s))
601         continue;
602       pname = id2str(pool, s->name);
603       if (strncmp(pname, "patch:", 6) != 0)
604         continue;
605
606       if (*mypatch)
607         {
608           if (strncmp(mypatch, pname + 6, strlen(pname + 6)) != 0)
609             continue;
610           if (strcmp(mypatch, pname + 6) != 0)
611             {
612               l = strlen(pname + 6);
613               if (mypatch[l] != '-')
614                 continue;
615               if (strcmp(mypatch + l + 1, id2str(pool, s->evr)) != 0)
616                 continue;
617             }
618         }
619       else
620         {
621           FOR_PROVIDES(p, pp, s->name)
622             {
623               Solvable *s2 = pool->solvables + p;
624               if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) < 0)
625                 break;
626             }
627           if (p) {
628             /* printf("found a newer one for %s\n", pname+6); */
629             continue;   /* found a newer one */
630           }
631         }
632       tests++;
633       if (!s->conflicts)
634         continue;
635
636 #if 0
637       printf("testing patch %s-%s\n", pname + 6, id2str(pool, s->evr));
638 #endif
639
640       test_all_old_patches_included(&c, pid);
641       test_all_packages_installable(&c, pid);
642       test_can_upgrade_all_packages(&c, pid);
643       test_no_ga_package_fulfills_dependency(&c, pid);
644     }
645
646   exit(c.status);
647 }