Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_cudf.c
2065 views
1
/*-
2
* Copyright (c) 2013 Vsevolod Stakhov <[email protected]>
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
cudf_print_package_name(FILE *f, const char *name)
42
{
43
const char *p, *c;
44
int r = 0;
45
46
p = c = name;
47
while (*p) {
48
if (*p == '_') {
49
r += fprintf(f, "%.*s", (int)(p - c), c);
50
fputc('@', f);
51
r ++;
52
c = p + 1;
53
}
54
p ++;
55
}
56
if (p > c) {
57
r += fprintf(f, "%.*s", (int)(p - c), c);
58
}
59
60
return r;
61
}
62
63
static inline int
64
cudf_print_element(FILE *f, const char *line, bool has_next, int *column)
65
{
66
int ret = 0;
67
if (*column > 80) {
68
*column = 0;
69
ret += fprintf(f, "\n ");
70
}
71
72
ret += cudf_print_package_name(f, line);
73
74
if (has_next)
75
ret += fprintf(f, ", ");
76
else
77
ret += fprintf(f, "\n");
78
79
if (ret > 0)
80
*column += ret;
81
82
return (ret);
83
}
84
85
static inline int
86
cudf_print_conflict(FILE *f, const char *uid, int ver, bool has_next, int *column)
87
{
88
int ret = 0;
89
if (*column > 80) {
90
*column = 0;
91
ret += fprintf(f, "\n ");
92
}
93
94
ret += cudf_print_package_name(f, uid);
95
ret += fprintf(f, "=%d", ver);
96
97
if (has_next)
98
ret += fprintf(f, ", ");
99
else
100
ret += fprintf(f, "\n");
101
102
if (ret > 0)
103
*column += ret;
104
105
return (ret);
106
}
107
108
109
static int
110
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
int column = 0, ver;
117
118
if (fprintf(f, "package: ") < 0)
119
return (EPKG_FATAL);
120
121
if (cudf_print_package_name(f, pkg->uid) < 0)
122
return (EPKG_FATAL);
123
124
if (fprintf(f, "\nversion: %d\n", version) < 0)
125
return (EPKG_FATAL);
126
127
if (pkghash_count(pkg->depshash) > 0) {
128
if (fprintf(f, "depends: ") < 0)
129
return (EPKG_FATAL);
130
LL_FOREACH(pkg->depends, dep) {
131
if (cudf_print_element(f, dep->name,
132
column + 1 == pkghash_count(pkg->depshash), &column) < 0) {
133
return (EPKG_FATAL);
134
}
135
}
136
}
137
138
column = 0;
139
if (vec_len(&pkg->provides) > 0) {
140
if (fprintf(f, "provides: ") < 0)
141
return (EPKG_FATAL);
142
vec_foreach(pkg->provides, i) {
143
if (cudf_print_element(f, pkg->provides.d[i],
144
column + 1 == vec_len(&pkg->provides), &column) < 0) {
145
return (EPKG_FATAL);
146
}
147
}
148
}
149
150
column = 0;
151
if (pkghash_count(pkg->conflictshash) > 0 ||
152
(conflicts_chain->next != NULL &&
153
!conflicts_chain->next->cudf_emit_skip)) {
154
if (fprintf(f, "conflicts: ") < 0)
155
return (EPKG_FATAL);
156
LL_FOREACH(pkg->conflicts, conflict) {
157
if (cudf_print_element(f, conflict->uid,
158
(conflict->next != NULL), &column) < 0) {
159
return (EPKG_FATAL);
160
}
161
}
162
ver = 1;
163
LL_FOREACH(conflicts_chain, u) {
164
if (u->pkg != pkg && !u->cudf_emit_skip) {
165
if (cudf_print_conflict(f, pkg->uid, ver,
166
(u->next != NULL && u->next->pkg != pkg), &column) < 0) {
167
return (EPKG_FATAL);
168
}
169
}
170
ver ++;
171
}
172
}
173
174
if (fprintf(f, "installed: %s\n\n", pkg->type == PKG_INSTALLED ?
175
"true" : "false") < 0)
176
return (EPKG_FATAL);
177
178
return (EPKG_OK);
179
}
180
181
static int
182
cudf_emit_request_packages(const char *op, struct pkg_jobs *j, FILE *f)
183
{
184
struct pkg_job_request *req;
185
int column = 0, cnt = 0, max;
186
bool printed = false;
187
pkghash_it it;
188
189
max = pkghash_count(j->request_add);
190
if (fprintf(f, "%s: ", op) < 0)
191
return (EPKG_FATAL);
192
it = pkghash_iterator(j->request_add);
193
while (pkghash_next(&it)) {
194
req = it.value;
195
cnt++;
196
if (req->skip)
197
continue;
198
if (cudf_print_element(f, req->item->pkg->uid,
199
(max > cnt), &column) < 0) {
200
return (EPKG_FATAL);
201
}
202
printed = true;
203
}
204
205
if (!printed)
206
if (fputc('\n', f) < 0)
207
return (EPKG_FATAL);
208
209
column = 0;
210
printed = false;
211
if (fprintf(f, "remove: ") < 0)
212
return (EPKG_FATAL);
213
max = pkghash_count(j->request_delete);
214
it = pkghash_iterator(j->request_delete);
215
while (pkghash_next(&it)) {
216
req = it.value;
217
cnt++;
218
if (req->skip)
219
continue;
220
if (cudf_print_element(f, req->item->pkg->uid,
221
(max > cnt), &column) < 0) {
222
return (EPKG_FATAL);
223
}
224
printed = true;
225
}
226
227
if (!printed)
228
if (fputc('\n', f) < 0)
229
return (EPKG_FATAL);
230
231
return (EPKG_OK);
232
}
233
234
static int
235
pkg_cudf_version_cmp(struct pkg_job_universe_item *a, struct pkg_job_universe_item *b)
236
{
237
int ret;
238
239
ret = pkg_version_cmp(a->pkg->version, b->pkg->version);
240
if (ret == 0) {
241
/* Ignore remote packages whose versions are equal to ours */
242
if (a->pkg->type != PKG_INSTALLED)
243
a->cudf_emit_skip = true;
244
else if (b->pkg->type != PKG_INSTALLED)
245
b->cudf_emit_skip = true;
246
}
247
248
249
return (ret);
250
}
251
252
int
253
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
if (fprintf(f, "preamble: \n\n") < 0)
261
return (EPKG_FATAL);
262
263
hit = pkghash_iterator(j->universe->items);
264
while (pkghash_next(&hit)) {
265
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
DL_SORT(it, pkg_cudf_version_cmp);
281
282
version = 1;
283
LL_FOREACH(it, icur) {
284
if (!icur->cudf_emit_skip) {
285
pkg = icur->pkg;
286
287
if (cudf_emit_pkg(pkg, version ++, f, it) != EPKG_OK)
288
return (EPKG_FATAL);
289
}
290
}
291
}
292
293
if (fprintf(f, "request: \n") < 0)
294
return (EPKG_FATAL);
295
296
switch (t) {
297
case PKG_JOBS_FETCH:
298
case PKG_JOBS_INSTALL:
299
case PKG_JOBS_DEINSTALL:
300
case PKG_JOBS_AUTOREMOVE:
301
if (cudf_emit_request_packages("install", j, f) != EPKG_OK)
302
return (EPKG_FATAL);
303
break;
304
case PKG_JOBS_UPGRADE:
305
if (cudf_emit_request_packages("upgrade", j, f) != EPKG_OK)
306
return (EPKG_FATAL);
307
break;
308
}
309
return (EPKG_OK);
310
}
311
312
/*
313
* Perform backward conversion of an uid replacing '@' to '_'
314
*/
315
static char *
316
cudf_strdup(const char *in)
317
{
318
size_t len = strlen(in);
319
char *out, *d;
320
const char *s;
321
322
out = xmalloc(len + 1);
323
324
s = in;
325
d = out;
326
while (isspace(*s))
327
s++;
328
while (*s) {
329
if (!isspace(*s))
330
*d++ = (*s == '@') ? '_' : *s;
331
s++;
332
}
333
334
*d = '\0';
335
return (out);
336
}
337
338
static void
339
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
res = xcalloc(1, sizeof(struct pkg_solved));
347
348
res->items[0] = it_new;
349
res->type = type;
350
if (it_old != NULL)
351
res->items[1] = it_old;
352
353
vec_push(target, res);
354
}
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
pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
365
{
366
struct pkg_job_universe_item *it, *cur, *selected = NULL, *old = NULL, *head;
367
int ver, n;
368
369
it = pkg_jobs_universe_find(j->universe, entry->uid);
370
if (it == NULL) {
371
pkg_emit_error("package %s is found in CUDF output but not in the universe",
372
entry->uid);
373
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
ver = strtoul(entry->version, NULL, 10);
382
383
/* Find the old head, see the comment in `pkg_jobs_cudf_emit_file` */
384
cur = it;
385
do {
386
head = cur;
387
cur = cur->prev;
388
} while (cur->next != NULL);
389
390
n = 1;
391
LL_FOREACH(head, cur) {
392
if (n == ver) {
393
selected = cur;
394
break;
395
}
396
n ++;
397
}
398
399
if (selected == NULL) {
400
pkg_emit_error("package %s-%d is found in CUDF output but the "
401
"universe has no such version (only %d versions found)",
402
entry->uid, ver, n);
403
return (EPKG_FATAL);
404
}
405
406
if (n == 1) {
407
if (entry->installed && selected->pkg->type != PKG_INSTALLED) {
408
pkg_debug(3, "pkg_cudf: schedule installation of %s(%d)",
409
entry->uid, ver);
410
pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_INSTALL);
411
}
412
else if (!entry->installed && selected->pkg->type == PKG_INSTALLED) {
413
pkg_debug(3, "pkg_cudf: schedule removing of %s(%d)",
414
entry->uid, ver);
415
pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_DELETE);
416
}
417
}
418
else {
419
/* Define upgrade */
420
LL_FOREACH(head, cur) {
421
if (cur != selected) {
422
old = cur;
423
break;
424
}
425
}
426
pkg_debug(3, "pkg_cudf: schedule upgrade of %s(to %d)",
427
entry->uid, ver);
428
assert(old != NULL);
429
/* XXX: this is a hack due to iterators stupidity */
430
selected->pkg->old_version = old->pkg->version;
431
pkg_jobs_cudf_insert_res_job (&j->jobs, selected, old, PKG_SOLVED_UPGRADE);
432
}
433
434
return (EPKG_OK);
435
}
436
437
int
438
pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f)
439
{
440
char *line = NULL, *begin, *param, *value;
441
size_t linecap = 0;
442
struct pkg_cudf_entry cur_pkg;
443
444
memset(&cur_pkg, 0, sizeof(cur_pkg));
445
446
while (getline(&line, &linecap, f) > 0) {
447
/* Split line, cut spaces */
448
begin = line;
449
param = strsep(&begin, ": \t");
450
value = begin;
451
while(begin != NULL)
452
value = strsep(&begin, " \t");
453
454
if (STREQ(param, "package")) {
455
if (cur_pkg.uid != NULL) {
456
if (pkg_jobs_cudf_add_package(j, &cur_pkg) != EPKG_OK) {
457
free(line);
458
return (EPKG_FATAL);
459
}
460
}
461
cur_pkg.uid = cudf_strdup(value);
462
cur_pkg.was_installed = false;
463
cur_pkg.installed = false;
464
cur_pkg.version = NULL;
465
}
466
else if (STREQ(param, "version")) {
467
if (cur_pkg.uid == NULL) {
468
pkg_emit_error("version line has no corresponding uid in CUDF output");
469
free(line);
470
return (EPKG_FATAL);
471
}
472
cur_pkg.version = cudf_strdup(value);
473
}
474
else if (STREQ(param, "installed")) {
475
if (cur_pkg.uid == NULL) {
476
pkg_emit_error("installed line has no corresponding uid in CUDF output");
477
free(line);
478
return (EPKG_FATAL);
479
}
480
if (strncmp(value, "true", 4) == 0)
481
cur_pkg.installed = true;
482
}
483
else if (STREQ(param, "was-installed")) {
484
if (cur_pkg.uid == NULL) {
485
pkg_emit_error("was-installed line has no corresponding uid in CUDF output");
486
free(line);
487
return (EPKG_FATAL);
488
}
489
if (strncmp(value, "true", 4) == 0)
490
cur_pkg.was_installed = true;
491
}
492
}
493
494
if (cur_pkg.uid != NULL) {
495
if (pkg_jobs_cudf_add_package(j, &cur_pkg) != EPKG_OK) {
496
free(line);
497
return (EPKG_FATAL);
498
}
499
}
500
501
free(line);
502
503
return (EPKG_OK);
504
}
505
506