LCOV - code coverage report
Current view: top level - libpkg - pkg_cudf.c (source / functions) Hit Total Coverage
Test: cov.info Lines: 0 243 0.0 %
Date: 2015-08-15 Functions: 0 11 0.0 %

          Line data    Source code
       1             : /*-
       2             :  * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
       3             :  * All rights reserved.
       4             :  *
       5             :  * Redistribution and use in source and binary forms, with or without
       6             :  * modification, are permitted provided that the following conditions
       7             :  * are met:
       8             :  * 1. Redistributions of source code must retain the above copyright
       9             :  *    notice, this list of conditions and the following disclaimer
      10             :  *    in this position and unchanged.
      11             :  * 2. Redistributions in binary form must reproduce the above copyright
      12             :  *    notice, this list of conditions and the following disclaimer in the
      13             :  *    documentation and/or other materials provided with the distribution.
      14             :  *
      15             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
      16             :  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      17             :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      18             :  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
      19             :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      20             :  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      21             :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      22             :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      23             :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
      24             :  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      25             :  */
      26             : 
      27             : #define _WITH_GETLINE
      28             : #include <stdio.h>
      29             : #include <ctype.h>
      30             : 
      31             : #include "pkg.h"
      32             : #include "private/event.h"
      33             : #include "private/pkg.h"
      34             : #include "private/pkgdb.h"
      35             : #include "private/pkg_jobs.h"
      36             : 
      37             : /*
      38             :  * CUDF does not support packages with '_' in theirs names, therefore
      39             :  * use this ugly function to replace '_' to '@'
      40             :  */
      41             : static inline int
      42           0 : cudf_print_package_name(FILE *f, const char *name)
      43             : {
      44             :         const char *p, *c;
      45           0 :         int r = 0;
      46             : 
      47           0 :         p = c = name;
      48           0 :         while (*p) {
      49           0 :                 if (*p == '_') {
      50           0 :                         r += fprintf(f, "%.*s", (int)(p - c), c);
      51           0 :                         fputc('@', f);
      52           0 :                         r ++;
      53           0 :                         c = p + 1;
      54             :                 }
      55           0 :                 p ++;
      56             :         }
      57           0 :         if (p > c) {
      58           0 :                 r += fprintf(f, "%.*s", (int)(p - c), c);
      59             :         }
      60             : 
      61           0 :         return r;
      62             : }
      63             : 
      64             : static inline int
      65           0 : cudf_print_element(FILE *f, const char *line, bool has_next, int *column)
      66             : {
      67           0 :         int ret = 0;
      68           0 :         if (*column > 80) {
      69           0 :                 *column = 0;
      70           0 :                 ret += fprintf(f, "\n ");
      71             :         }
      72             : 
      73           0 :         ret += cudf_print_package_name(f, line);
      74             : 
      75           0 :         if (has_next)
      76           0 :                 ret += fprintf(f, ", ");
      77             :         else
      78           0 :                 ret += fprintf(f, "\n");
      79             : 
      80           0 :         if (ret > 0)
      81           0 :                 *column += ret;
      82             : 
      83           0 :         return (ret);
      84             : }
      85             : 
      86             : static inline int
      87           0 : cudf_print_conflict(FILE *f, const char *uid, int ver, bool has_next, int *column)
      88             : {
      89           0 :         int ret = 0;
      90           0 :         if (*column > 80) {
      91           0 :                 *column = 0;
      92           0 :                 ret += fprintf(f, "\n ");
      93             :         }
      94             : 
      95           0 :         ret += cudf_print_package_name(f, uid);
      96           0 :         ret += fprintf(f, "=%d", ver);
      97             : 
      98           0 :         if (has_next)
      99           0 :                 ret += fprintf(f, ", ");
     100             :         else
     101           0 :                 ret += fprintf(f, "\n");
     102             : 
     103           0 :         if (ret > 0)
     104           0 :                 *column += ret;
     105             : 
     106           0 :         return (ret);
     107             : }
     108             : 
     109             : 
     110             : static int
     111           0 : cudf_emit_pkg(struct pkg *pkg, int version, FILE *f,
     112             :                 struct pkg_job_universe_item *conflicts_chain)
     113             : {
     114             :         struct pkg_dep *dep;
     115             :         struct pkg_conflict *conflict, *ctmp;
     116             :         struct pkg_job_universe_item *u;
     117             :         char *buf;
     118           0 :         int column = 0, ver;
     119             : 
     120           0 :         if (fprintf(f, "package: ") < 0)
     121           0 :                 return (EPKG_FATAL);
     122             : 
     123           0 :         if (cudf_print_package_name(f, pkg->uid) < 0)
     124           0 :                 return (EPKG_FATAL);
     125             : 
     126           0 :         if (fprintf(f, "\nversion: %d\n", version) < 0)
     127           0 :                 return (EPKG_FATAL);
     128             : 
     129           0 :         if (kh_count(pkg->deps) > 0) {
     130           0 :                 if (fprintf(f, "depends: ") < 0)
     131           0 :                         return (EPKG_FATAL);
     132           0 :                 kh_each_value(pkg->deps, dep, {
     133             :                         if (cudf_print_element(f, dep->name,
     134             :                             column + 1 == kh_count(pkg->deps), &column) < 0) {
     135             :                                 return (EPKG_FATAL);
     136             :                         }
     137             :                 });
     138             :         }
     139             : 
     140           0 :         column = 0;
     141           0 :         if (kh_count(pkg->provides) > 0) {
     142           0 :                 if (fprintf(f, "provides: ") < 0)
     143           0 :                         return (EPKG_FATAL);
     144           0 :                 kh_each_value(pkg->provides, buf, {
     145             :                         if (cudf_print_element(f, buf,
     146             :                             column + 1 == kh_count(pkg->provides), &column) < 0) {
     147             :                                 return (EPKG_FATAL);
     148             :                         }
     149             :                 });
     150             :         }
     151             : 
     152           0 :         column = 0;
     153           0 :         if (HASH_COUNT(pkg->conflicts) > 0 ||
     154           0 :                         (conflicts_chain->next != NULL &&
     155           0 :                         conflicts_chain->next->priority != INT_MIN)) {
     156           0 :                 if (fprintf(f, "conflicts: ") < 0)
     157           0 :                         return (EPKG_FATAL);
     158           0 :                 HASH_ITER(hh, pkg->conflicts, conflict, ctmp) {
     159           0 :                         if (cudf_print_element(f, conflict->uid,
     160           0 :                                         (conflict->hh.next != NULL), &column) < 0) {
     161           0 :                                 return (EPKG_FATAL);
     162             :                         }
     163             :                 }
     164           0 :                 ver = 1;
     165           0 :                 LL_FOREACH(conflicts_chain, u) {
     166           0 :                         if (u->pkg != pkg && u->priority != INT_MIN) {
     167           0 :                                 if (cudf_print_conflict(f, pkg->uid, ver,
     168           0 :                                    (u->next != NULL && u->next->pkg != pkg), &column) < 0) {
     169           0 :                                         return (EPKG_FATAL);
     170             :                                 }
     171             :                         }
     172           0 :                         ver ++;
     173             :                 }
     174             :         }
     175             : 
     176           0 :         if (fprintf(f, "installed: %s\n\n", pkg->type == PKG_INSTALLED ?
     177             :                         "true" : "false") < 0)
     178           0 :                 return (EPKG_FATAL);
     179             : 
     180           0 :         return (EPKG_OK);
     181             : }
     182             : 
     183             : static int
     184           0 : cudf_emit_request_packages(const char *op, struct pkg_jobs *j, FILE *f)
     185             : {
     186             :         struct pkg_job_request *req, *tmp;
     187           0 :         int column = 0;
     188           0 :         bool printed = false;
     189             : 
     190           0 :         if (fprintf(f, "%s: ", op) < 0)
     191           0 :                 return (EPKG_FATAL);
     192           0 :         HASH_ITER(hh, j->request_add, req, tmp) {
     193           0 :                 if (req->skip)
     194           0 :                         continue;
     195           0 :                 if (cudf_print_element(f, req->item->pkg->uid,
     196           0 :                     (req->hh.next != NULL), &column) < 0) {
     197           0 :                         return (EPKG_FATAL);
     198             :                 }
     199           0 :                 printed = true;
     200             :         }
     201             : 
     202           0 :         if (!printed)
     203           0 :                 if (fputc('\n', f) < 0)
     204           0 :                         return (EPKG_FATAL);
     205             : 
     206           0 :         column = 0;
     207           0 :         printed = false;
     208           0 :         if (fprintf(f, "remove: ") < 0)
     209           0 :                 return (EPKG_FATAL);
     210           0 :         HASH_ITER(hh, j->request_delete, req, tmp) {
     211           0 :                 if (req->skip)
     212           0 :                         continue;
     213           0 :                 if (cudf_print_element(f, req->item->pkg->uid,
     214           0 :                     (req->hh.next != NULL), &column) < 0) {
     215           0 :                         return (EPKG_FATAL);
     216             :                 }
     217           0 :                 printed = true;
     218             :         }
     219             : 
     220           0 :         if (!printed)
     221           0 :                 if (fputc('\n', f) < 0)
     222           0 :                         return (EPKG_FATAL);
     223             : 
     224           0 :         return (EPKG_OK);
     225             : }
     226             : 
     227             : static int
     228           0 : pkg_cudf_version_cmp(struct pkg_job_universe_item *a, struct pkg_job_universe_item *b)
     229             : {
     230             :         int ret;
     231             : 
     232           0 :         ret = pkg_version_cmp(a->pkg->version, b->pkg->version);
     233           0 :         if (ret == 0) {
     234             :                 /* Ignore remote packages whose versions are equal to ours */
     235           0 :                 if (a->pkg->type != PKG_INSTALLED)
     236           0 :                         a->priority = INT_MIN;
     237           0 :                 else if (b->pkg->type != PKG_INSTALLED)
     238           0 :                         b->priority = INT_MIN;
     239             :         }
     240             : 
     241             : 
     242           0 :         return (ret);
     243             : }
     244             : 
     245             : int
     246           0 : pkg_jobs_cudf_emit_file(struct pkg_jobs *j, pkg_jobs_t t, FILE *f)
     247             : {
     248             :         struct pkg *pkg;
     249             :         struct pkg_job_universe_item *it, *itmp, *icur;
     250             :         int version;
     251             : 
     252           0 :         if (fprintf(f, "preamble: \n\n") < 0)
     253           0 :                 return (EPKG_FATAL);
     254             : 
     255           0 :         HASH_ITER(hh, j->universe->items, it, itmp) {
     256             :                 /* XXX
     257             :                  * Here are dragons:
     258             :                  * after sorting it we actually modify the head of the list, but there is
     259             :                  * no simple way to update a pointer in uthash, therefore universe hash
     260             :                  * contains not a head of list but a random elt of the conflicts chain:
     261             :                  * before:
     262             :                  * head -> elt1 -> elt2 -> elt3
     263             :                  * after:
     264             :                  * elt1 -> elt3 -> head -> elt2
     265             :                  *
     266             :                  * But hash would still point to head whilst the real head is elt1.
     267             :                  * So after sorting we need to rotate conflicts chain back to find the new
     268             :                  * head.
     269             :                  */
     270           0 :                 DL_SORT(it, pkg_cudf_version_cmp);
     271             : 
     272           0 :                 version = 1;
     273           0 :                 LL_FOREACH(it, icur) {
     274           0 :                         if (icur->priority != INT_MIN) {
     275           0 :                                 pkg = icur->pkg;
     276             : 
     277           0 :                                 if (cudf_emit_pkg(pkg, version ++, f, it) != EPKG_OK)
     278           0 :                                         return (EPKG_FATAL);
     279             :                         }
     280             :                 }
     281             :         }
     282             : 
     283           0 :         if (fprintf(f, "request: \n") < 0)
     284           0 :                         return (EPKG_FATAL);
     285             : 
     286           0 :         switch (t) {
     287             :         case PKG_JOBS_FETCH:
     288             :         case PKG_JOBS_INSTALL:
     289             :         case PKG_JOBS_DEINSTALL:
     290             :         case PKG_JOBS_AUTOREMOVE:
     291           0 :                 if (cudf_emit_request_packages("install", j, f) != EPKG_OK)
     292           0 :                         return (EPKG_FATAL);
     293           0 :                 break;
     294             :         case PKG_JOBS_UPGRADE:
     295           0 :                 if (cudf_emit_request_packages("upgrade", j, f) != EPKG_OK)
     296           0 :                         return (EPKG_FATAL);
     297           0 :                 break;
     298             :         }
     299           0 :         return (EPKG_OK);
     300             : }
     301             : 
     302             : /*
     303             :  * Perform backward conversion of an uid replacing '@' to '_'
     304             :  */
     305             : static char *
     306           0 : cudf_strdup(const char *in)
     307             : {
     308           0 :         size_t len = strlen(in);
     309             :         char *out, *d;
     310             :         const char *s;
     311             : 
     312           0 :         out = malloc(len + 1);
     313           0 :         if (out == NULL)
     314           0 :                 return (NULL);
     315             : 
     316           0 :         s = in;
     317           0 :         d = out;
     318           0 :         while (isspace(*s))
     319           0 :                 s++;
     320           0 :         while (*s) {
     321           0 :                 if (!isspace(*s))
     322           0 :                         *d++ = (*s == '@') ? '_' : *s;
     323           0 :                 s++;
     324             :         }
     325             : 
     326           0 :         *d = '\0';
     327           0 :         return (out);
     328             : }
     329             : 
     330             : static void
     331           0 : pkg_jobs_cudf_insert_res_job (struct pkg_solved **target,
     332             :                 struct pkg_job_universe_item *it_new,
     333             :                 struct pkg_job_universe_item *it_old,
     334             :                 int type)
     335             : {
     336             :         struct pkg_solved *res;
     337             : 
     338           0 :         res = calloc(1, sizeof(struct pkg_solved));
     339           0 :         if (res == NULL) {
     340           0 :                 pkg_emit_errno("calloc", "pkg_solved");
     341           0 :                 return;
     342             :         }
     343             : 
     344           0 :         res->items[0] = it_new;
     345           0 :         res->type = type;
     346           0 :         if (it_old != NULL)
     347           0 :                 res->items[1] = it_old;
     348             : 
     349           0 :         DL_APPEND(*target, res);
     350             : }
     351             : 
     352             : struct pkg_cudf_entry {
     353             :         char *uid;
     354             :         bool was_installed;
     355             :         bool installed;
     356             :         char *version;
     357             : };
     358             : 
     359             : static int
     360           0 : pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
     361             : {
     362           0 :         struct pkg_job_universe_item *it, *cur, *selected = NULL, *old = NULL, *head;
     363             :         int ver, n;
     364             : 
     365           0 :         it = pkg_jobs_universe_find(j->universe, entry->uid);
     366           0 :         if (it == NULL) {
     367           0 :                 pkg_emit_error("package %s is found in CUDF output but not in the universe",
     368             :                                 entry->uid);
     369           0 :                 return (EPKG_FATAL);
     370             :         }
     371             : 
     372             :         /*
     373             :          * Now we need to select an appropriate version. We assume that
     374             :          * the order of packages in list is the same as was passed to the
     375             :          * cudf solver.
     376             :          */
     377           0 :         ver = strtoul(entry->version, NULL, 10);
     378             : 
     379             :         /* Find the old head, see the comment in `pkg_jobs_cudf_emit_file` */
     380           0 :         cur = it;
     381             :         do {
     382           0 :                 head = cur;
     383           0 :                 cur = cur->prev;
     384           0 :         } while (cur->next != NULL);
     385             : 
     386           0 :         n = 1;
     387           0 :         LL_FOREACH(head, cur) {
     388           0 :                 if (n == ver) {
     389           0 :                         selected = cur;
     390           0 :                         break;
     391             :                 }
     392           0 :                 n ++;
     393             :         }
     394             : 
     395           0 :         if (selected == NULL) {
     396           0 :                 pkg_emit_error("package %s-%d is found in CUDF output but the "
     397             :                                 "universe has no such version (only %d versions found)",
     398             :                                 entry->uid, ver, n);
     399           0 :                 return (EPKG_FATAL);
     400             :         }
     401             : 
     402           0 :         if (n == 1) {
     403           0 :                 if (entry->installed && selected->pkg->type != PKG_INSTALLED) {
     404           0 :                         pkg_debug(3, "pkg_cudf: schedule installation of %s(%d)",
     405             :                                         entry->uid, ver);
     406           0 :                         pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_INSTALL);
     407           0 :                         j->count ++;
     408             :                 }
     409           0 :                 else if (!entry->installed && selected->pkg->type == PKG_INSTALLED) {
     410           0 :                         pkg_debug(3, "pkg_cudf: schedule removing of %s(%d)",
     411             :                                         entry->uid, ver);
     412           0 :                         pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_DELETE);
     413           0 :                         j->count ++;
     414             :                 }
     415             :         }
     416             :         else {
     417             :                 /* Define upgrade */
     418           0 :                 LL_FOREACH(head, cur) {
     419           0 :                         if (cur != selected) {
     420           0 :                                 old = cur;
     421           0 :                                 break;
     422             :                         }
     423             :                 }
     424           0 :                 pkg_debug(3, "pkg_cudf: schedule upgrade of %s(to %d)",
     425             :                                 entry->uid, ver);
     426           0 :                 assert(old != NULL);
     427             :                 /* XXX: this is a hack due to iterators stupidity */
     428           0 :                 selected->pkg->old_version = old->pkg->version;
     429           0 :                 pkg_jobs_cudf_insert_res_job (&j->jobs, selected, old, PKG_SOLVED_UPGRADE);
     430           0 :                 j->count ++;
     431             :         }
     432             : 
     433           0 :         return (EPKG_OK);
     434             : }
     435             : 
     436             : int
     437           0 : pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f)
     438             : {
     439           0 :         char *line = NULL, *begin, *param, *value;
     440           0 :         size_t linecap = 0;
     441             :         ssize_t linelen;
     442             :         struct pkg_cudf_entry cur_pkg;
     443             : 
     444           0 :         memset(&cur_pkg, 0, sizeof(cur_pkg));
     445             : 
     446           0 :         while ((linelen = getline(&line, &linecap, f)) > 0) {
     447             :                 /* Split line, cut spaces */
     448           0 :                 begin = line;
     449           0 :                 param = strsep(&begin, ": \t");
     450           0 :                 value = begin;
     451           0 :                 while(begin != NULL)
     452           0 :                         value = strsep(&begin, " \t");
     453             : 
     454           0 :                 if (strcmp(param, "package") == 0) {
     455           0 :                         if (cur_pkg.uid != NULL) {
     456           0 :                                 if (pkg_jobs_cudf_add_package(j, &cur_pkg) != EPKG_OK)  {
     457           0 :                                         free(line);
     458           0 :                                         return (EPKG_FATAL);
     459             :                                 }
     460             :                         }
     461           0 :                         cur_pkg.uid = cudf_strdup(value);
     462           0 :                         cur_pkg.was_installed = false;
     463           0 :                         cur_pkg.installed = false;
     464           0 :                         cur_pkg.version = NULL;
     465             :                 }
     466           0 :                 else if (strcmp(param, "version") == 0) {
     467           0 :                         if (cur_pkg.uid == NULL) {
     468           0 :                                 pkg_emit_error("version line has no corresponding uid in CUDF output");
     469           0 :                                 free(line);
     470           0 :                                 return (EPKG_FATAL);
     471             :                         }
     472           0 :                         cur_pkg.version = cudf_strdup(value);
     473             :                 }
     474           0 :                 else if (strcmp(param, "installed") == 0) {
     475           0 :                         if (cur_pkg.uid == NULL) {
     476           0 :                                 pkg_emit_error("installed line has no corresponding uid in CUDF output");
     477           0 :                                 free(line);
     478           0 :                                 return (EPKG_FATAL);
     479             :                         }
     480           0 :                         if (strncmp(value, "true", 4) == 0)
     481           0 :                                 cur_pkg.installed = true;
     482             :                 }
     483           0 :                 else if (strcmp(param, "was-installed") == 0) {
     484           0 :                         if (cur_pkg.uid == NULL) {
     485           0 :                                 pkg_emit_error("was-installed line has no corresponding uid in CUDF output");
     486           0 :                                 free(line);
     487           0 :                                 return (EPKG_FATAL);
     488             :                         }
     489           0 :                         if (strncmp(value, "true", 4) == 0)
     490           0 :                                 cur_pkg.was_installed = true;
     491             :                 }
     492             :         }
     493             : 
     494           0 :         if (cur_pkg.uid != NULL) {
     495           0 :                 if (pkg_jobs_cudf_add_package(j, &cur_pkg) != EPKG_OK)  {
     496           0 :                         free(line);
     497           0 :                         return (EPKG_FATAL);
     498             :                 }
     499             :         }
     500             : 
     501           0 :         free(line);
     502             : 
     503           0 :         return (EPKG_OK);
     504             : }

Generated by: LCOV version 1.10