1 /*
2 * Copyright (c) 2013, SUSE Inc.
3 *
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
6 */
7
8 /*
9 * linkedpkg.c
10 *
11 * Linked packages are "pseudo" packages that are bound to real packages but
12 * contain different information (name/summary/description). They are normally
13 * somehow generated from the real packages, either when the repositories are
14 * created or automatically from the packages by looking at the provides.
15 *
16 * We currently support:
17 *
18 * application:
19 * created from AppStream appdata xml in the repository (which is generated
20 * from files in /usr/share/appdata)
21 *
22 * product:
23 * created from product data in the repository (which is generated from files
24 * in /etc/products.d). In the future we may switch to using product()
25 * provides of packages.
26 *
27 * pattern:
28 * created from pattern() provides of packages.
29 *
30 */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <assert.h>
37
38 #include "pool.h"
39 #include "repo.h"
40 #include "solver.h"
41 #include "evr.h"
42 #include "bitmap.h"
43 #include "linkedpkg.h"
44
45 #ifdef ENABLE_LINKED_PKGS
46
47 void
find_application_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)48 find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
49 {
50 Id req = 0;
51 Id prv = 0;
52 Id p, pp;
53 Id pkgname = 0, appdataid = 0;
54
55 /* find appdata requires */
56 if (s->requires)
57 {
58 Id *reqp = s->repo->idarraydata + s->requires;
59 while ((req = *reqp++) != 0) /* go through all requires */
60 {
61 if (ISRELDEP(req))
62 continue;
63 if (!strncmp("appdata(", pool_id2str(pool, req), 8))
64 appdataid = req;
65 else
66 pkgname = req;
67 }
68 }
69 req = appdataid ? appdataid : pkgname;
70 if (!req)
71 return;
72 /* find application-appdata provides */
73 if (s->provides)
74 {
75 Id *prvp = s->repo->idarraydata + s->provides;
76 const char *reqs = pool_id2str(pool, req);
77 const char *prvs;
78 while ((prv = *prvp++) != 0) /* go through all provides */
79 {
80 if (ISRELDEP(prv))
81 continue;
82 prvs = pool_id2str(pool, prv);
83 if (strncmp("application-appdata(", prvs, 20))
84 continue;
85 if (appdataid)
86 {
87 if (!strcmp(prvs + 12, reqs))
88 break;
89 }
90 else
91 {
92 int reqsl = strlen(reqs);
93 if (!strncmp(prvs + 20, reqs, reqsl) && !strcmp(prvs + 20 + reqsl, ")"))
94 break;
95 }
96 }
97 }
98 if (!prv)
99 return; /* huh, no provides found? */
100 /* now link em */
101 FOR_PROVIDES(p, pp, req)
102 if (pool->solvables[p].repo == s->repo)
103 if (!pkgname || pool->solvables[p].name == pkgname)
104 queue_push(qr, p);
105 if (!qr->count && pkgname && appdataid)
106 {
107 /* huh, no matching package? try without pkgname filter */
108 FOR_PROVIDES(p, pp, req)
109 if (pool->solvables[p].repo == s->repo)
110 queue_push(qr, p);
111 }
112 if (qp)
113 {
114 FOR_PROVIDES(p, pp, prv)
115 if (pool->solvables[p].repo == s->repo)
116 queue_push(qp, p);
117 }
118 if (reqidp)
119 *reqidp = req;
120 if (prvidp)
121 *prvidp = prv;
122 }
123
124 void
find_product_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)125 find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
126 {
127 Id p, pp, namerelid;
128 char *str;
129 unsigned int sbt = 0;
130
131 /* search for project requires */
132 namerelid = 0;
133 if (s->requires)
134 {
135 Id req, *reqp = s->repo->idarraydata + s->requires;
136 const char *nn = pool_id2str(pool, s->name);
137 int nnl = strlen(nn);
138 while ((req = *reqp++) != 0) /* go through all requires */
139 if (ISRELDEP(req))
140 {
141 const char *rn;
142 Reldep *rd = GETRELDEP(pool, req);
143 if (rd->flags != REL_EQ || rd->evr != s->evr)
144 continue;
145 rn = pool_id2str(pool, rd->name);
146 if (!strncmp(rn, "product(", 8) && !strncmp(rn + 8, nn + 8, nnl - 8) && !strcmp( rn + nnl, ")"))
147 {
148 namerelid = req;
149 break;
150 }
151 }
152 }
153 if (!namerelid)
154 {
155 /* too bad. construct from scratch */
156 str = pool_tmpjoin(pool, pool_id2str(pool, s->name), ")", 0);
157 str[7] = '(';
158 namerelid = pool_rel2id(pool, pool_str2id(pool, str, 1), s->evr, REL_EQ, 1);
159 }
160 FOR_PROVIDES(p, pp, namerelid)
161 {
162 Solvable *ps = pool->solvables + p;
163 if (ps->repo != s->repo || ps->arch != s->arch)
164 continue;
165 queue_push(qr, p);
166 }
167 if (qr->count > 1)
168 {
169 /* multiple providers. try buildtime filter */
170 sbt = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
171 if (sbt)
172 {
173 unsigned int bt;
174 int i, j;
175 int filterqp = 1;
176 for (i = j = 0; i < qr->count; i++)
177 {
178 bt = solvable_lookup_num(pool->solvables + qr->elements[i], SOLVABLE_BUILDTIME, 0);
179 if (!bt)
180 filterqp = 0; /* can't filter */
181 if (!bt || bt == sbt)
182 qr->elements[j++] = qr->elements[i];
183 }
184 if (j)
185 qr->count = j;
186 if (!j || !filterqp)
187 sbt = 0; /* filter failed */
188 }
189 }
190 if (!qr->count && s->repo == pool->installed)
191 {
192 /* oh no! Look up reference file */
193 Dataiterator di;
194 const char *refbasename = solvable_lookup_str(s, PRODUCT_REFERENCEFILE);
195 if (refbasename)
196 {
197 dataiterator_init(&di, pool, s->repo, 0, SOLVABLE_FILELIST, refbasename, SEARCH_STRING);
198 while (dataiterator_step(&di))
199 {
200 if (di.key->type != REPOKEY_TYPE_DIRSTRARRAY)
201 continue;
202 if (strcmp(repodata_dir2str(di.data, di.kv.id, 0), "/etc/products.d") != 0)
203 continue;
204 queue_push(qr, di.solvid);
205 }
206 dataiterator_free(&di);
207 if (qp)
208 {
209 dataiterator_init(&di, pool, s->repo, 0, PRODUCT_REFERENCEFILE, refbasename, SEARCH_STRING);
210 while (dataiterator_step(&di))
211 queue_push(qp, di.solvid);
212 dataiterator_free(&di);
213 }
214 }
215 }
216 else if (qp)
217 {
218 /* find qp */
219 FOR_PROVIDES(p, pp, s->name)
220 {
221 Solvable *ps = pool->solvables + p;
222 if (s->name != ps->name || ps->repo != s->repo || ps->arch != s->arch || s->evr != ps->evr)
223 continue;
224 if (sbt && solvable_lookup_num(ps, SOLVABLE_BUILDTIME, 0) != sbt)
225 continue;
226 queue_push(qp, p);
227 }
228 }
229 if (reqidp)
230 *reqidp = namerelid;
231 if (prvidp)
232 *prvidp = solvable_selfprovidedep(s);
233 }
234
235 void
find_pattern_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)236 find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
237 {
238 Id p, pp, *pr, apevr = 0, aprel = 0;
239
240 /* check if autopattern */
241 if (!s->provides)
242 return;
243 for (pr = s->repo->idarraydata + s->provides; (p = *pr++) != 0; )
244 if (ISRELDEP(p))
245 {
246 Reldep *rd = GETRELDEP(pool, p);
247 if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
248 {
249 aprel = p;
250 apevr = rd->evr;
251 break;
252 }
253 }
254 if (!apevr)
255 return;
256 FOR_PROVIDES(p, pp, apevr)
257 {
258 Solvable *s2 = pool->solvables + p;
259 if (s2->repo == s->repo && s2->name == apevr && s2->evr == s->evr && s2->vendor == s->vendor)
260 queue_push(qr, p);
261 }
262 if (qp)
263 {
264 FOR_PROVIDES(p, pp, aprel)
265 {
266 Solvable *s2 = pool->solvables + p;
267 if (s2->repo == s->repo && s2->evr == s->evr && s2->vendor == s->vendor)
268 queue_push(qp, p);
269 }
270 }
271 if (reqidp)
272 *reqidp = apevr;
273 if (prvidp)
274 *prvidp = aprel;
275 }
276
277 /* the following two functions are used in solvable_lookup_str_base to do
278 * translated lookups on the product/pattern packages
279 */
280 Id
find_autopattern_name(Pool * pool,Solvable * s)281 find_autopattern_name(Pool *pool, Solvable *s)
282 {
283 Id prv, *prvp;
284 if (!s->provides)
285 return 0;
286 for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
287 if (ISRELDEP(prv))
288 {
289 Reldep *rd = GETRELDEP(pool, prv);
290 if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
291 return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0;
292 }
293 return 0;
294 }
295
296 Id
find_autoproduct_name(Pool * pool,Solvable * s)297 find_autoproduct_name(Pool *pool, Solvable *s)
298 {
299 Id prv, *prvp;
300 if (!s->provides)
301 return 0;
302 for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
303 if (ISRELDEP(prv))
304 {
305 Reldep *rd = GETRELDEP(pool, prv);
306 if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autoproduct()"))
307 return strncmp(pool_id2str(pool, rd->evr), "product:", 8) != 0 ? rd->evr : 0;
308 }
309 return 0;
310 }
311
312 void
find_package_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)313 find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
314 {
315 const char *name = pool_id2str(pool, s->name);
316 if (name[0] == 'a' && !strncmp("application:", name, 12))
317 find_application_link(pool, s, reqidp, qr, prvidp, qp);
318 else if (name[0] == 'p' && !strncmp("pattern:", name, 7))
319 find_pattern_link(pool, s, reqidp, qr, prvidp, qp);
320 else if (name[0] == 'p' && !strncmp("product:", name, 8))
321 find_product_link(pool, s, reqidp, qr, prvidp, qp);
322 }
323
324 static int
name_min_max(Pool * pool,Solvable * s,Id * namep,Id * minp,Id * maxp)325 name_min_max(Pool *pool, Solvable *s, Id *namep, Id *minp, Id *maxp)
326 {
327 Queue q;
328 Id qbuf[4];
329 Id name, min, max;
330 int i;
331
332 queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
333 find_package_link(pool, s, 0, &q, 0, 0);
334 if (!q.count)
335 {
336 queue_free(&q);
337 return 0;
338 }
339 s = pool->solvables + q.elements[0];
340 name = s->name;
341 min = max = s->evr;
342 for (i = 1; i < q.count; i++)
343 {
344 s = pool->solvables + q.elements[i];
345 if (s->name != name)
346 {
347 queue_free(&q);
348 return 0;
349 }
350 if (s->evr == min || s->evr == max)
351 continue;
352 if (pool_evrcmp(pool, min, s->evr, EVRCMP_COMPARE) >= 0)
353 min = s->evr;
354 else if (min == max || pool_evrcmp(pool, max, s->evr, EVRCMP_COMPARE) <= 0)
355 max = s->evr;
356 }
357 queue_free(&q);
358 *namep = name;
359 *minp = min;
360 *maxp = max;
361 return 1;
362 }
363
364 int
pool_link_evrcmp(Pool * pool,Solvable * s1,Solvable * s2)365 pool_link_evrcmp(Pool *pool, Solvable *s1, Solvable *s2)
366 {
367 Id name1, evrmin1, evrmax1;
368 Id name2, evrmin2, evrmax2;
369
370 if (s1->name != s2->name)
371 return 0; /* can't compare */
372 if (!name_min_max(pool, s1, &name1, &evrmin1, &evrmax1))
373 return 0;
374 if (!name_min_max(pool, s2, &name2, &evrmin2, &evrmax2))
375 return 0;
376 /* compare linked names */
377 if (name1 != name2)
378 return 0;
379 if (evrmin1 == evrmin2 && evrmax1 == evrmax2)
380 return 0;
381 /* now compare evr intervals */
382 if (evrmin1 == evrmax1 && evrmin2 == evrmax2)
383 return pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE);
384 if (evrmin1 != evrmax2 && pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE) > 0)
385 return 1;
386 if (evrmax1 != evrmin2 && pool_evrcmp(pool, evrmax1, evrmin2, EVRCMP_COMPARE) < 0)
387 return -1;
388 return 0;
389 }
390
391 void
extend_updatemap_to_buddies(Solver * solv)392 extend_updatemap_to_buddies(Solver *solv)
393 {
394 Pool *pool = solv->pool;
395 Repo *installed = solv->installed;
396 Solvable *s;
397 int p, ip;
398
399 if (!installed)
400 return;
401 if (!solv->updatemap.size || !solv->instbuddy)
402 return;
403 FOR_REPO_SOLVABLES(installed, p, s)
404 {
405 if (!MAPTST(&solv->updatemap, p - installed->start))
406 continue;
407 if ((ip = solv->instbuddy[p - installed->start]) <= 1)
408 continue;
409 if (!has_package_link(pool, s)) /* only look at pseudo -> real relations */
410 continue;
411 if (ip < installed->start || ip >= installed->end || pool->solvables[ip].repo != installed)
412 continue; /* just in case... */
413 MAPSET(&solv->updatemap, ip - installed->start);
414 }
415 }
416
417 #endif
418