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