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 (tll_length(pkg->provides) > 0) {
140 [ # # ]: 0 : if (fprintf(f, "provides: ") < 0)
141 : 0 : return (EPKG_FATAL);
142 [ # # # # : 0 : tll_foreach(pkg->provides, p) {
# # ]
143 [ # # # # : 0 : if (cudf_print_element(f, p->item,
# # ]
144 : 0 : column + 1 == tll_length(pkg->provides), &column) < 0) {
145 : 0 : return (EPKG_FATAL);
146 : : }
147 : 0 : }
148 : 0 : }
149 : :
150 : 0 : column = 0;
151 [ # # # # ]: 0 : if (pkghash_count(pkg->conflictshash) > 0 ||
152 [ # # ]: 0 : (conflicts_chain->next != NULL &&
153 : 0 : !conflicts_chain->next->cudf_emit_skip)) {
154 [ # # ]: 0 : if (fprintf(f, "conflicts: ") < 0)
155 : 0 : return (EPKG_FATAL);
156 [ # # ]: 0 : LL_FOREACH(pkg->conflicts, conflict) {
157 [ # # # # : 0 : if (cudf_print_element(f, conflict->uid,
# # ]
158 : 0 : (conflict->next != NULL), &column) < 0) {
159 : 0 : return (EPKG_FATAL);
160 : : }
161 : 0 : }
162 : 0 : ver = 1;
163 [ # # ]: 0 : LL_FOREACH(conflicts_chain, u) {
164 [ # # # # ]: 0 : if (u->pkg != pkg && !u->cudf_emit_skip) {
165 [ # # # # ]: 0 : if (cudf_print_conflict(f, pkg->uid, ver,
166 [ # # # # ]: 0 : (u->next != NULL && u->next->pkg != pkg), &column) < 0) {
167 : 0 : return (EPKG_FATAL);
168 : : }
169 : 0 : }
170 : 0 : ver ++;
171 : 0 : }
172 : 0 : }
173 : :
174 [ # # # # ]: 0 : if (fprintf(f, "installed: %s\n\n", pkg->type == PKG_INSTALLED ?
175 : 0 : "true" : "false") < 0)
176 : 0 : return (EPKG_FATAL);
177 : :
178 : 0 : return (EPKG_OK);
179 : 0 : }
180 : :
181 : : static int
182 : 0 : cudf_emit_request_packages(const char *op, struct pkg_jobs *j, FILE *f)
183 : : {
184 : : struct pkg_job_request *req;
185 : 0 : int column = 0, cnt = 0, max;
186 : 0 : bool printed = false;
187 : : pkghash_it it;
188 : :
189 : 0 : max = pkghash_count(j->request_add);
190 [ # # ]: 0 : if (fprintf(f, "%s: ", op) < 0)
191 : 0 : return (EPKG_FATAL);
192 : 0 : it = pkghash_iterator(j->request_add);
193 [ # # ]: 0 : while (pkghash_next(&it)) {
194 : 0 : req = it.value;
195 : 0 : cnt++;
196 [ # # ]: 0 : if (req->skip)
197 : 0 : continue;
198 [ # # # # : 0 : if (cudf_print_element(f, req->item->pkg->uid,
# # ]
199 : 0 : (max > cnt), &column) < 0) {
200 : 0 : return (EPKG_FATAL);
201 : : }
202 : 0 : printed = true;
203 : : }
204 : :
205 [ # # ]: 0 : if (!printed)
206 [ # # ]: 0 : if (fputc('\n', f) < 0)
207 : 0 : return (EPKG_FATAL);
208 : :
209 : 0 : column = 0;
210 : 0 : printed = false;
211 [ # # ]: 0 : if (fprintf(f, "remove: ") < 0)
212 : 0 : return (EPKG_FATAL);
213 : 0 : max = pkghash_count(j->request_delete);
214 : 0 : it = pkghash_iterator(j->request_delete);
215 [ # # ]: 0 : while (pkghash_next(&it)) {
216 : 0 : req = it.value;
217 : 0 : cnt++;
218 [ # # ]: 0 : if (req->skip)
219 : 0 : continue;
220 [ # # # # : 0 : if (cudf_print_element(f, req->item->pkg->uid,
# # ]
221 : 0 : (max > cnt), &column) < 0) {
222 : 0 : return (EPKG_FATAL);
223 : : }
224 : 0 : printed = true;
225 : : }
226 : :
227 [ # # ]: 0 : if (!printed)
228 [ # # ]: 0 : if (fputc('\n', f) < 0)
229 : 0 : return (EPKG_FATAL);
230 : :
231 : 0 : return (EPKG_OK);
232 : 0 : }
233 : :
234 : : static int
235 : 0 : pkg_cudf_version_cmp(struct pkg_job_universe_item *a, struct pkg_job_universe_item *b)
236 : : {
237 : : int ret;
238 : :
239 : 0 : ret = pkg_version_cmp(a->pkg->version, b->pkg->version);
240 [ # # ]: 0 : if (ret == 0) {
241 : : /* Ignore remote packages whose versions are equal to ours */
242 [ # # ]: 0 : if (a->pkg->type != PKG_INSTALLED)
243 : 0 : a->cudf_emit_skip = true;
244 [ # # ]: 0 : else if (b->pkg->type != PKG_INSTALLED)
245 : 0 : b->cudf_emit_skip = true;
246 : 0 : }
247 : :
248 : :
249 : 0 : return (ret);
250 : : }
251 : :
252 : : int
253 : 0 : pkg_jobs_cudf_emit_file(struct pkg_jobs *j, pkg_jobs_t t, FILE *f)
254 : : {
255 : : struct pkg *pkg;
256 : : struct pkg_job_universe_item *it, *icur;
257 : : int version;
258 : : pkghash_it hit;
259 : :
260 [ # # ]: 0 : if (fprintf(f, "preamble: \n\n") < 0)
261 : 0 : return (EPKG_FATAL);
262 : :
263 : 0 : hit = pkghash_iterator(j->universe->items);
264 [ # # ]: 0 : while (pkghash_next(&hit)) {
265 : 0 : it = (struct pkg_job_universe_item *)hit.value;
266 : : /* XXX
267 : : * Here are dragons:
268 : : * after sorting it we actually modify the head of the list, but there is
269 : : * no simple way to update a pointer in uthash, therefore universe hash
270 : : * contains not a head of list but a random elt of the conflicts chain:
271 : : * before:
272 : : * head -> elt1 -> elt2 -> elt3
273 : : * after:
274 : : * elt1 -> elt3 -> head -> elt2
275 : : *
276 : : * But hash would still point to head whilst the real head is elt1.
277 : : * So after sorting we need to rotate conflicts chain back to find the new
278 : : * head.
279 : : */
280 [ # # # # : 0 : DL_SORT(it, pkg_cudf_version_cmp);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
281 : :
282 : 0 : version = 1;
283 [ # # ]: 0 : LL_FOREACH(it, icur) {
284 [ # # ]: 0 : if (!icur->cudf_emit_skip) {
285 : 0 : pkg = icur->pkg;
286 : :
287 [ # # ]: 0 : if (cudf_emit_pkg(pkg, version ++, f, it) != EPKG_OK)
288 : 0 : return (EPKG_FATAL);
289 : 0 : }
290 : 0 : }
291 : : }
292 : :
293 [ # # ]: 0 : if (fprintf(f, "request: \n") < 0)
294 : 0 : return (EPKG_FATAL);
295 : :
296 [ # # # # : 0 : switch (t) {
# # ]
297 : : case PKG_JOBS_FETCH:
298 : : case PKG_JOBS_INSTALL:
299 : : case PKG_JOBS_DEINSTALL:
300 : : case PKG_JOBS_AUTOREMOVE:
301 [ # # ]: 0 : if (cudf_emit_request_packages("install", j, f) != EPKG_OK)
302 : 0 : return (EPKG_FATAL);
303 : 0 : break;
304 : : case PKG_JOBS_UPGRADE:
305 [ # # ]: 0 : if (cudf_emit_request_packages("upgrade", j, f) != EPKG_OK)
306 : 0 : return (EPKG_FATAL);
307 : 0 : break;
308 : : }
309 : 0 : return (EPKG_OK);
310 : 0 : }
311 : :
312 : : /*
313 : : * Perform backward conversion of an uid replacing '@' to '_'
314 : : */
315 : : static char *
316 : 0 : cudf_strdup(const char *in)
317 : : {
318 : 0 : size_t len = strlen(in);
319 : : char *out, *d;
320 : : const char *s;
321 : :
322 : 0 : out = xmalloc(len + 1);
323 : :
324 : 0 : s = in;
325 : 0 : d = out;
326 [ # # ]: 0 : while (isspace(*s))
327 : 0 : s++;
328 [ # # ]: 0 : while (*s) {
329 [ # # ]: 0 : if (!isspace(*s))
330 [ # # ]: 0 : *d++ = (*s == '@') ? '_' : *s;
331 : 0 : s++;
332 : : }
333 : :
334 : 0 : *d = '\0';
335 : 0 : return (out);
336 : : }
337 : :
338 : : static void
339 : 0 : pkg_jobs_cudf_insert_res_job (pkg_solved_list *target,
340 : : struct pkg_job_universe_item *it_new,
341 : : struct pkg_job_universe_item *it_old,
342 : : int type)
343 : : {
344 : : struct pkg_solved *res;
345 : :
346 : 0 : res = xcalloc(1, sizeof(struct pkg_solved));
347 : :
348 : 0 : res->items[0] = it_new;
349 : 0 : res->type = type;
350 [ # # ]: 0 : if (it_old != NULL)
351 : 0 : res->items[1] = it_old;
352 : :
353 [ # # # # : 0 : tll_push_back(*target, res);
# # # # #
# ]
354 : 0 : }
355 : :
356 : : struct pkg_cudf_entry {
357 : : char *uid;
358 : : bool was_installed;
359 : : bool installed;
360 : : char *version;
361 : : };
362 : :
363 : : static int
364 : 0 : pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
365 : : {
366 : 0 : struct pkg_job_universe_item *it, *cur, *selected = NULL, *old = NULL, *head;
367 : : int ver, n;
368 : :
369 : 0 : it = pkg_jobs_universe_find(j->universe, entry->uid);
370 [ # # ]: 0 : if (it == NULL) {
371 : 0 : pkg_emit_error("package %s is found in CUDF output but not in the universe",
372 : 0 : entry->uid);
373 : 0 : return (EPKG_FATAL);
374 : : }
375 : :
376 : : /*
377 : : * Now we need to select an appropriate version. We assume that
378 : : * the order of packages in list is the same as was passed to the
379 : : * cudf solver.
380 : : */
381 : 0 : ver = strtoul(entry->version, NULL, 10);
382 : :
383 : : /* Find the old head, see the comment in `pkg_jobs_cudf_emit_file` */
384 : 0 : cur = it;
385 : 0 : do {
386 : 0 : head = cur;
387 : 0 : cur = cur->prev;
388 [ # # ]: 0 : } while (cur->next != NULL);
389 : :
390 : 0 : n = 1;
391 [ # # ]: 0 : LL_FOREACH(head, cur) {
392 [ # # ]: 0 : if (n == ver) {
393 : 0 : selected = cur;
394 : 0 : break;
395 : : }
396 : 0 : n ++;
397 : 0 : }
398 : :
399 [ # # ]: 0 : if (selected == NULL) {
400 : 0 : pkg_emit_error("package %s-%d is found in CUDF output but the "
401 : : "universe has no such version (only %d versions found)",
402 : 0 : entry->uid, ver, n);
403 : 0 : return (EPKG_FATAL);
404 : : }
405 : :
406 [ # # ]: 0 : if (n == 1) {
407 [ # # # # ]: 0 : if (entry->installed && selected->pkg->type != PKG_INSTALLED) {
408 : 0 : pkg_debug(3, "pkg_cudf: schedule installation of %s(%d)",
409 : 0 : entry->uid, ver);
410 : 0 : pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_INSTALL);
411 : 0 : }
412 [ # # # # ]: 0 : else if (!entry->installed && selected->pkg->type == PKG_INSTALLED) {
413 : 0 : pkg_debug(3, "pkg_cudf: schedule removing of %s(%d)",
414 : 0 : entry->uid, ver);
415 : 0 : pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_DELETE);
416 : 0 : }
417 : 0 : }
418 : : else {
419 : : /* Define upgrade */
420 [ # # ]: 0 : LL_FOREACH(head, cur) {
421 [ # # ]: 0 : if (cur != selected) {
422 : 0 : old = cur;
423 : 0 : break;
424 : : }
425 : 0 : }
426 : 0 : pkg_debug(3, "pkg_cudf: schedule upgrade of %s(to %d)",
427 : 0 : entry->uid, ver);
428 [ # # ]: 0 : assert(old != NULL);
429 : : /* XXX: this is a hack due to iterators stupidity */
430 : 0 : selected->pkg->old_version = old->pkg->version;
431 : 0 : pkg_jobs_cudf_insert_res_job (&j->jobs, selected, old, PKG_SOLVED_UPGRADE);
432 : : }
433 : :
434 : 0 : return (EPKG_OK);
435 : 0 : }
436 : :
437 : : int
438 : 0 : pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f)
439 : : {
440 : 0 : char *line = NULL, *begin, *param, *value;
441 : 0 : size_t linecap = 0;
442 : : struct pkg_cudf_entry cur_pkg;
443 : :
444 : 0 : memset(&cur_pkg, 0, sizeof(cur_pkg));
445 : :
446 [ # # ]: 0 : while (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 (STREQ(param, "package")) {
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 : 0 : }
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 : 0 : }
466 [ # # ]: 0 : else if (STREQ(param, "version")) {
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 : 0 : }
474 [ # # ]: 0 : else if (STREQ(param, "installed")) {
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 : 0 : }
483 [ # # ]: 0 : else if (STREQ(param, "was-installed")) {
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 : 0 : }
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 : 0 : }
500 : :
501 : 0 : free(line);
502 : :
503 : 0 : return (EPKG_OK);
504 : 0 : }
|