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 : }
|