/ libpkg / pkg_jobs_universe.c
pkg_jobs_universe.c
   1  /* Copyright (c) 2014, Vsevolod Stakhov
   2   * All rights reserved.
   3   *
   4   * Redistribution and use in source and binary forms, with or without
   5   * modification, are permitted provided that the following conditions are met:
   6   *       * Redistributions of source code must retain the above copyright
   7   *         notice, this list of conditions and the following disclaimer.
   8   *       * Redistributions in binary form must reproduce the above copyright
   9   *         notice, this list of conditions and the following disclaimer in the
  10   *         documentation and/or other materials provided with the distribution.
  11   *
  12   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
  13   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  14   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  15   * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  16   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  17   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  18   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  20   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  21   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22   */
  23  
  24  #ifdef HAVE_CONFIG_H
  25  #include "pkg_config.h"
  26  #endif
  27  
  28  #define dbg(x, ...) pkg_dbg(PKG_DBG_UNIVERSE, x, __VA_ARGS__)
  29  
  30  #include <sys/param.h>
  31  #include <sys/types.h>
  32  
  33  #include <assert.h>
  34  #include <errno.h>
  35  #if __has_include(<libutil.h>)
  36  #include <libutil.h>
  37  #endif
  38  #include <stdbool.h>
  39  #include <stdlib.h>
  40  #include <string.h>
  41  #include <ctype.h>
  42  
  43  #include "pkg.h"
  44  #include "private/event.h"
  45  #include "private/pkg.h"
  46  #include "private/pkgdb.h"
  47  #include "private/pkg_jobs.h"
  48  
  49  #define IS_DELETE(j) ((j)->type == PKG_JOBS_DEINSTALL || (j)->type == PKG_JOBS_AUTOREMOVE)
  50  
  51  struct pkg *
  52  pkg_jobs_universe_get_local(struct pkg_jobs_universe *universe,
  53  	const char *uid, unsigned flag)
  54  {
  55  	struct pkg *pkg = NULL;
  56  	struct pkgdb_it *it;
  57  	struct pkg_job_universe_item *unit, *cur, *found;
  58  
  59  	if (flag == 0) {
  60  		flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_RDEPS|PKG_LOAD_OPTIONS|
  61  			PKG_LOAD_REQUIRES|PKG_LOAD_PROVIDES|
  62  			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|PKG_LOAD_ANNOTATIONS|
  63  			PKG_LOAD_CONFLICTS;
  64  	}
  65  
  66  	unit = pkghash_get_value(universe->items, uid);
  67  	if (unit != NULL) {
  68  		/* Search local in a universe chain */
  69  		cur = unit;
  70  		found = NULL;
  71  		do {
  72  			if (cur->pkg->type == PKG_INSTALLED || cur->pkg->type == PKG_GROUP_INSTALLED) {
  73  				found = cur;
  74  				break;
  75  			}
  76  			cur = cur->prev;
  77  		} while (cur != unit);
  78  
  79  		if (found && found->pkg->type == PKG_INSTALLED) {
  80  			pkgdb_ensure_loaded(universe->j->db, found->pkg, flag);
  81  			return (found->pkg);
  82  		}
  83  	}
  84  
  85  	/* XX TODO query local groups */
  86  	if ((it = pkgdb_query(universe->j->db, uid, MATCH_INTERNAL)) == NULL)
  87  		return (NULL);
  88  
  89  	if (pkgdb_it_next(it, &pkg, flag) != EPKG_OK)
  90  		pkg = NULL;
  91  
  92  	pkgdb_it_free(it);
  93  
  94  	return (pkg);
  95  }
  96  
  97  static pkgs_t *
  98  pkg_jobs_universe_get_remote(struct pkg_jobs_universe *universe,
  99  	const char *uid, unsigned flag)
 100  {
 101  	struct pkg *pkg = NULL;
 102  	pkgs_t *result = NULL;
 103  	struct pkgdb_it *it;
 104  	struct pkg_job_universe_item *unit, *cur, *found;
 105  
 106  	if (flag == 0) {
 107  		flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_OPTIONS|
 108  			PKG_LOAD_PROVIDES|PKG_LOAD_REQUIRES|
 109  				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
 110  				PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
 111  	}
 112  
 113  	unit = pkghash_get_value(universe->items, uid);
 114  	if (unit != NULL && unit->pkg->type != PKG_INSTALLED) {
 115  		/* Search local in a universe chain */
 116  		cur = unit;
 117  		found = NULL;
 118  		do {
 119  			if (cur->pkg->type != PKG_INSTALLED) {
 120  				found = cur;
 121  				break;
 122  			}
 123  			cur = cur->prev;
 124  		} while (cur != unit);
 125  
 126  		if (found) {
 127  			/* Assume processed */
 128  			return (NULL);
 129  		}
 130  	}
 131  
 132  	if ((it = pkgdb_repo_query2(universe->j->db, uid, MATCH_INTERNAL,
 133  		universe->j->reponames)) == NULL)
 134  		return (NULL);
 135  
 136  	while (pkgdb_it_next(it, &pkg, flag) == EPKG_OK) {
 137  		if (result == NULL)
 138  			result = xcalloc(1, sizeof(pkgs_t));
 139  		append_pkg_if_newer(result, pkg);
 140  		pkg = NULL;
 141  	}
 142  
 143  	pkgdb_it_free(it);
 144  
 145  	return (result);
 146  }
 147  
 148  /**
 149   * Check whether a package is in the universe already or add it
 150   * @return item or NULL
 151   */
 152  int
 153  pkg_jobs_universe_add_pkg(struct pkg_jobs_universe *universe, struct pkg *pkg,
 154      struct pkg_job_universe_item **found)
 155  {
 156  	struct pkg_job_universe_item *item, *seen, *tmp = NULL;
 157  
 158  	pkg_validate(pkg, universe->j->db);
 159  
 160  	if (pkg->digest == NULL) {
 161  		dbg(3, "no digest found for package %s (%s-%s)",
 162  		    pkg->uid, pkg->name, pkg->version);
 163  		if (pkg_checksum_calculate(pkg, universe->j->db, false, true, false) != EPKG_OK) {
 164  			if (found != NULL)
 165  				*found = NULL;
 166  			return (EPKG_FATAL);
 167  		}
 168  	}
 169  
 170  	seen = pkghash_get_value(universe->seen, pkg->digest);
 171  	if (seen) {
 172  		bool same_package = false;
 173  
 174  		DL_FOREACH(seen, tmp) {
 175  			if (tmp->pkg == pkg || (tmp->pkg->type == pkg->type &&
 176  			    STREQ(tmp->pkg->digest, pkg->digest))) {
 177  				if (tmp->pkg->reponame != NULL) {
 178  					if (STREQ(tmp->pkg->reponame, pkg->reponame)) {
 179  						same_package = true;
 180  						break;
 181  					}
 182  				} else {
 183  					same_package = true;
 184  					break;
 185  				}
 186  			}
 187  		}
 188  
 189  		if (same_package) {
 190  			if (found != NULL) {
 191  				*found = seen;
 192  			}
 193  
 194  			return (EPKG_END);
 195  		}
 196  	}
 197  
 198  	if (pkg_is_locked(pkg)) {
 199  		return (EPKG_LOCKED);
 200  	}
 201  
 202  	dbg(2, "add new %s pkg: %s, (%s-%s:%s)",
 203  	    (pkg->type == PKG_INSTALLED ? "local" : "remote"), pkg->uid,
 204  	    pkg->name, pkg->version, pkg->digest);
 205  
 206  	item = xcalloc(1, sizeof (struct pkg_job_universe_item));
 207  	item->pkg = pkg;
 208  
 209  	tmp = pkghash_get_value(universe->items, pkg->uid);
 210  	if (tmp == NULL) {
 211  		pkghash_safe_add(universe->items, pkg->uid, item, NULL);
 212  		item->inhash = true;
 213  	}
 214  
 215  	DL_APPEND(tmp, item);
 216  
 217  	if (seen == NULL)
 218  		pkghash_safe_add(universe->seen, item->pkg->digest, item, NULL);
 219  
 220  	universe->nitems++;
 221  
 222  	if (found != NULL)
 223  		*found = item;
 224  
 225  	return (EPKG_OK);
 226  }
 227  
 228  #define DEPS_FLAG_REVERSE 0x1 << 1
 229  #define DEPS_FLAG_MIRROR 0x1 << 2
 230  #define DEPS_FLAG_FORCE_LOCAL 0x1 << 3
 231  #define DEPS_FLAG_FORCE_MISSING 0x1 << 4
 232  #define DEPS_FLAG_FORCE_UPGRADE 0x1 << 5
 233  
 234  static int
 235  pkg_jobs_universe_process_deps(struct pkg_jobs_universe *universe,
 236  	struct pkg *pkg, unsigned flags)
 237  {
 238  	struct pkg_dep *d = NULL;
 239  	int (*deps_func)(const struct pkg *pkg, struct pkg_dep **d);
 240  	int rc;
 241  	struct pkg_job_universe_item *unit;
 242  	struct pkg *npkg, *rpkg, *lpkg;
 243  	pkgs_t *rpkgs = NULL;
 244  	bool found = false;
 245  
 246  	rpkg = NULL;
 247  
 248  	if (flags & DEPS_FLAG_REVERSE) {
 249  		dbg(4, "Processing rdeps for %s (%s)", pkg->uid, pkg->type == PKG_INSTALLED ? "installed" : "remote");
 250  		if (pkg->type != PKG_INSTALLED) {
 251  			lpkg = pkg_jobs_universe_get_local(universe, pkg->uid, 0);
 252  			if (lpkg != NULL && lpkg != pkg)
 253  				return (pkg_jobs_universe_process_deps(universe, lpkg, flags));
 254  		}
 255  		deps_func = pkg_rdeps;
 256  	}
 257  	else {
 258  		dbg(4, "Processing deps for %s", pkg->uid);
 259  		deps_func = pkg_deps;
 260  	}
 261  
 262  	while (deps_func(pkg, &d) == EPKG_OK) {
 263  		dbg(4, "Processing *deps for %s: %s", pkg->uid, d->uid);
 264  		if (pkghash_get(universe->items, d->uid) != NULL)
 265  			continue;
 266  
 267  		rpkgs = NULL;
 268  		npkg = NULL;
 269  		if (!(flags & DEPS_FLAG_MIRROR)) {
 270  			npkg = pkg_jobs_universe_get_local(universe, d->uid, 0);
 271  		}
 272  
 273  		if (!(flags & DEPS_FLAG_FORCE_LOCAL)) {
 274  
 275  			/* Check for remote dependencies */
 276  			rpkgs = pkg_jobs_universe_get_remote(universe, d->uid, 0);
 277  		}
 278  
 279  		if (npkg == NULL && rpkgs == NULL) {
 280  			pkg_emit_error("%s has a missing dependency: %s",
 281  				pkg->name, d->name);
 282  
 283  			if (flags & DEPS_FLAG_FORCE_MISSING) {
 284  				continue;
 285  			}
 286  
 287  			return (EPKG_FATAL);
 288  		}
 289  
 290  		if (npkg != NULL) {
 291  			if (pkg_jobs_universe_process_item(universe, npkg, &unit) != EPKG_OK) {
 292  				continue;
 293  			}
 294  		}
 295  
 296  		if (rpkgs == NULL)
 297  			continue;
 298  		/*
 299  		 * When processing deps, we should first try to select a dependency
 300  		 * from the same repo.
 301  		 * Otherwise, we would have ping-pong of dependencies instead of
 302  		 * the situation when this behaviour is handled by
 303  		 * CONSERVATIVE_UPGRADES.
 304  		 *
 305  		 * Important notes here:
 306  		 * 1. We are looking for packages that are dependencies of a package
 307  		 * `pkg`
 308  		 * 2. Now if `pkg` belongs to repo `r` and `rpkg` belongs to repo
 309  		 * `r` then we just select it.
 310  		 * 3. If `rpkg` is not found in `r` we just scan all packages
 311  		 */
 312  
 313  		/*
 314  		 * XXX: this is the proper place to expand flexible dependencies
 315  		 */
 316  
 317  		found = false;
 318  		/* Iteration one */
 319  		vec_rforeach(*rpkgs, i) {
 320  			rpkg = rpkgs->d[i];
 321  
 322  			if (pkg->reponame && rpkg->reponame &&
 323  					STREQ(pkg->reponame, rpkg->reponame)) {
 324  				found = true;
 325  				break;
 326  			}
 327  		}
 328  
 329  		/* Fallback if a dependency is not found in the same repo */
 330  		if (!found) {
 331  			vec_rforeach(*rpkgs, i) {
 332  				rpkg = rpkgs->d[i];
 333  
 334  				if (npkg != NULL) {
 335  					/* Set reason for upgrades */
 336  					if (!pkg_jobs_need_upgrade(&universe->j->system_shlibs, rpkg, npkg))
 337  						continue;
 338  					/* Save automatic flag */
 339  					rpkg->automatic = npkg->automatic;
 340  				}
 341  
 342  				rc = pkg_jobs_universe_process_item(universe, rpkg, NULL);
 343  
 344  				/* Special case if we cannot find any package */
 345  				if (npkg == NULL && rc != EPKG_OK) {
 346  					vec_free(rpkgs);
 347  					free(rpkgs);
 348  					return (rc);
 349  				}
 350  			}
 351  		}
 352  		else {
 353  			assert (rpkg != NULL);
 354  
 355  			if (npkg != NULL) {
 356  				/* Set reason for upgrades */
 357  				if (!pkg_jobs_need_upgrade(&universe->j->system_shlibs, rpkg, npkg))
 358  					continue;
 359  				/* Save automatic flag */
 360  				rpkg->automatic = npkg->automatic;
 361  			}
 362  
 363  			rc = pkg_jobs_universe_process_item(universe, rpkg, NULL);
 364  			if (npkg == NULL && rc != EPKG_OK) {
 365  				vec_free(rpkgs);
 366  				free(rpkgs);
 367  				return (rc);
 368  			}
 369  		}
 370  
 371  		vec_free(rpkgs);
 372  		free(rpkgs);
 373  	}
 374  
 375  	return (EPKG_OK);
 376  }
 377  
 378  static int
 379  pkg_jobs_universe_handle_provide(struct pkg_jobs_universe *universe,
 380      struct pkgdb_it *it, const char *name, bool is_shlib)
 381  {
 382  	struct pkg_job_universe_item *unit;
 383  	struct pkg_job_provide *pr, *prhead;
 384  	struct pkg *npkg, *rpkg;
 385  	int rc;
 386  	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
 387  				PKG_LOAD_REQUIRES|PKG_LOAD_PROVIDES|
 388  				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
 389  				PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
 390  
 391  	rpkg = NULL;
 392  
 393  	prhead = pkghash_get_value(universe->provides, name);
 394  	while (pkgdb_it_next(it, &rpkg, flags) == EPKG_OK) {
 395  		dbg(4, "handle_provide: processing package %s for %s %s",
 396  		    rpkg->uid, is_shlib ? "shlib" : "provide", name);
 397  
 398  		/* Check for local packages */
 399  		if ((unit = pkghash_get_value(universe->items, rpkg->uid)) != NULL) {
 400  			dbg(4, "handle_provide: package %s already in universe", rpkg->uid);
 401  			/*
 402  			 * Skip adding the remote package if a local version
 403  			 * with the same digest already exists in the universe:
 404  			 * there is nothing to upgrade.
 405  			 */
 406  			if (unit->pkg->type == PKG_INSTALLED &&
 407  			    unit->pkg->digest != NULL &&
 408  			    rpkg->digest != NULL &&
 409  			    STREQ(unit->pkg->digest, rpkg->digest)) {
 410  				dbg(4, "handle_provide: %s remote identical to local, skipping", rpkg->uid);
 411  				pkg_free(rpkg);
 412  				rpkg = NULL;
 413  				goto provide;
 414  			}
 415  			if (pkg_jobs_universe_process_item(universe, rpkg,
 416  			    &unit) != EPKG_OK) {
 417  				continue;
 418  			}
 419  
 420  			rpkg = NULL;
 421  		}
 422  		else {
 423  			/* Maybe local package has just been not added */
 424  			npkg = pkg_jobs_universe_get_local(universe, rpkg->uid, 0);
 425  			if (npkg != NULL) {
 426  				dbg(4, "handle_provide: found local package %s", npkg->uid);
 427  				if (pkg_jobs_universe_process_item(universe, npkg,
 428  						&unit) != EPKG_OK) {
 429  					return (EPKG_FATAL);
 430  				}
 431  				/*
 432  				 * Skip adding the remote package if the local
 433  				 * version has the same digest.
 434  				 */
 435  				if (npkg->digest != NULL &&
 436  				    rpkg->digest != NULL &&
 437  				    STREQ(npkg->digest, rpkg->digest)) {
 438  					dbg(4, "handle_provide: %s remote identical to local, skipping", rpkg->uid);
 439  					pkg_free(rpkg);
 440  					rpkg = NULL;
 441  					goto provide;
 442  				}
 443  				if (pkg_jobs_universe_process_item(universe, rpkg,
 444  						&unit) != EPKG_OK) {
 445  					continue;
 446  				}
 447  				if (unit != NULL)
 448  					rpkg = NULL;
 449  			}
 450  		}
 451  
 452  		/* Skip seen packages */
 453  		if (unit == NULL) {
 454  			if (rpkg->digest == NULL) {
 455  				dbg(3, "no digest found for package %s", rpkg->uid);
 456  				if (pkg_checksum_calculate(rpkg,
 457  				    universe->j->db, false, true, false) != EPKG_OK) {
 458  					return (EPKG_FATAL);
 459  				}
 460  			}
 461  			rc = pkg_jobs_universe_process_item(universe, rpkg,
 462  					&unit);
 463  
 464  			if (rc != EPKG_OK) {
 465  				return (rc);
 466  			}
 467  
 468  			/* Reset package to avoid freeing */
 469  			rpkg = NULL;
 470  		}
 471  
 472  provide:
 473  		pr = xcalloc (1, sizeof (*pr));
 474  		pr->un = unit;
 475  		pr->provide = name;
 476  		pr->is_shlib = is_shlib;
 477  
 478  		if (prhead == NULL) {
 479  			DL_APPEND(prhead, pr);
 480  			pkghash_safe_add(universe->provides, pr->provide,
 481  			    prhead, NULL);
 482  			dbg(4, "add new provide %s-%s(%s) for require %s",
 483  					pr->un->pkg->name, pr->un->pkg->version,
 484  					pr->un->pkg->type == PKG_INSTALLED ? "l" : "r",
 485  					pr->provide);
 486  		} else {
 487  			DL_APPEND(prhead, pr);
 488  			dbg(4, "append provide %s-%s(%s) for require %s",
 489  					pr->un->pkg->name, pr->un->pkg->version,
 490  					pr->un->pkg->type == PKG_INSTALLED ? "l" : "r",
 491  					pr->provide);
 492  		}
 493  	}
 494  
 495  	return (EPKG_OK);
 496  }
 497  
 498  static int
 499  pkg_jobs_universe_process_shlibs(struct pkg_jobs_universe *universe,
 500  	struct pkg *pkg)
 501  {
 502  	struct pkgdb_it *it;
 503  	int rc;
 504  
 505  	dbg(4, "process_shlibs: processing %zu shlibs for %s",
 506  	    vec_len(&pkg->shlibs_required), pkg->uid);
 507  
 508  	vec_foreach(pkg->shlibs_required, i) {
 509  		const char *s = pkg->shlibs_required.d[i];
 510  		if (charv_search(&universe->j->system_shlibs, s) != NULL) {
 511  			dbg(4, "process_shlibs: %s is a system shlib, skipping", s);
 512  			continue;
 513  		}
 514  		if (pkghash_get(universe->provides, s) != NULL) {
 515  			dbg(4, "process_shlibs: %s already in provides hash, skipping", s);
 516  			continue;
 517  		}
 518  
 519  		dbg(4, "process_shlibs: looking for providers of %s for %s", s, pkg->uid);
 520  
 521  		/* Check for local provides */
 522  		it = pkgdb_query_shlib_provide(universe->j->db, s);
 523  		if (it != NULL) {
 524  			rc = pkg_jobs_universe_handle_provide(universe, it, s,
 525  			    true);
 526  			pkgdb_it_free(it);
 527  
 528  			if (rc != EPKG_OK) {
 529  				dbg(1, "cannot find local packages that provide library %s "
 530  						"required for %s",
 531  						s, pkg->name);
 532  			}
 533  		}
 534  		/* Not found, search in the repos */
 535  		it = pkgdb_repo_shlib_provide(universe->j->db,
 536  			s, universe->j->reponames);
 537  
 538  		if (it != NULL) {
 539  			rc = pkg_jobs_universe_handle_provide(universe, it, s,
 540  			    true);
 541  			pkgdb_it_free(it);
 542  
 543  			if (rc != EPKG_OK) {
 544  				dbg(1, "cannot find remote packages that provide library %s "
 545  						"required for %s",
 546  				    s, pkg->name);
 547  			}
 548  		}
 549  	}
 550  
 551  	return (EPKG_OK);
 552  }
 553  
 554  static int
 555  pkg_jobs_universe_process_provides_requires(struct pkg_jobs_universe *universe,
 556  	struct pkg *pkg)
 557  {
 558  	struct pkgdb_it *it;
 559  	int rc;
 560  
 561  	dbg(4, "process_requires: processing %zu requires for %s",
 562  	    vec_len(&pkg->requires), pkg->uid);
 563  
 564  	vec_foreach(pkg->requires, i) {
 565  		const char *r = pkg->requires.d[i];
 566  		if (pkghash_get(universe->provides, r) != NULL) {
 567  			dbg(4, "process_requires: %s already in provides hash, skipping", r);
 568  			continue;
 569  		}
 570  
 571  		dbg(4, "process_requires: looking for providers of %s for %s", r, pkg->uid);
 572  
 573  		/* Check for local provides */
 574  		it = pkgdb_query_provide(universe->j->db, r);
 575  		if (it != NULL) {
 576  			rc = pkg_jobs_universe_handle_provide(universe, it, r,
 577  			    false);
 578  			pkgdb_it_free(it);
 579  
 580  			if (rc != EPKG_OK) {
 581  				dbg(1, "cannot find local packages that provide %s "
 582  						"required for %s",
 583  						r, pkg->name);
 584  			}
 585  		}
 586  
 587  		/* Not found, search in the repos */
 588  		it = pkgdb_repo_provide(universe->j->db,
 589  			r, universe->j->reponames);
 590  
 591  		if (it != NULL) {
 592  			rc = pkg_jobs_universe_handle_provide(universe, it, r,
 593  			    false);
 594  			pkgdb_it_free(it);
 595  
 596  			if (rc != EPKG_OK) {
 597  				dbg(1, "cannot find remote packages that provide %s "
 598  						"required for %s",
 599  				    r, pkg->name);
 600  				return (rc);
 601  			}
 602  		}
 603  	}
 604  
 605  	return (EPKG_OK);
 606  }
 607  
 608  int
 609  pkg_jobs_universe_process_item(struct pkg_jobs_universe *universe, struct pkg *pkg,
 610  		struct pkg_job_universe_item **result)
 611  {
 612  	unsigned flags = 0, job_flags;
 613  	int rc = EPKG_OK;
 614  	pkg_jobs_t type = universe->j->type;
 615  	struct pkg_job_universe_item *found;
 616  
 617  	dbg(4, "Processing item %s", pkg->uid);
 618  
 619  	job_flags = universe->j->flags;
 620  
 621  	/*
 622  	 * Add pkg itself. If package is already seen then we check the `processed`
 623  	 * flag that means that we have already tried to check our universe
 624  	 */
 625  	rc = pkg_jobs_universe_add_pkg(universe, pkg, &found);
 626  	if (rc == EPKG_CONFLICT)
 627  		return (rc);
 628  
 629  	if (result)
 630  		*result = found;
 631  
 632  	if (rc == EPKG_END) {
 633  		dbg(4, "Package %s already seen, processed=%d", pkg->uid, found->processed);
 634  		if (found->processed)
 635  			return (EPKG_OK);
 636  	}
 637  	else if (rc != EPKG_OK) {
 638  		return (rc);
 639  	}
 640  
 641  	found->processed = true;
 642  
 643  	/* Convert jobs flags to dependency logical flags */
 644  	if (job_flags & PKG_FLAG_FORCE_MISSING)
 645  		flags |= DEPS_FLAG_FORCE_MISSING;
 646  
 647  	switch(type) {
 648  	case PKG_JOBS_FETCH:
 649  		if (job_flags & PKG_FLAG_RECURSIVE) {
 650  			flags |= DEPS_FLAG_MIRROR;
 651  			/* For fetch jobs we worry about depends only */
 652  			rc = pkg_jobs_universe_process_deps(universe, pkg, flags);
 653  		}
 654  		break;
 655  	case PKG_JOBS_INSTALL:
 656  	case PKG_JOBS_UPGRADE:
 657  		/* Handle depends */
 658  		rc = pkg_jobs_universe_process_deps(universe, pkg, flags);
 659  		if (rc != EPKG_OK)
 660  			return (rc);
 661  		/*
 662  		 * Handle reverse depends.  Limit recursive expansion to
 663  		 * prevent the universe from growing exponentially
 664  		 * (target → dep → rdep → dep → rdep …).
 665  		 *
 666  		 * Process rdeps when:
 667  		 * - We are not inside rdeps processing yet (depth == 0), OR
 668  		 * - This is a remote package (an upgrade is being considered
 669  		 *   and its rdeps may need updating too).
 670  		 */
 671  		if (universe->rdeps_depth == 0 ||
 672  		    pkg->type != PKG_INSTALLED) {
 673  			universe->rdeps_depth++;
 674  			rc = pkg_jobs_universe_process_deps(universe, pkg,
 675  				flags|DEPS_FLAG_REVERSE);
 676  			universe->rdeps_depth--;
 677  			if (rc != EPKG_OK)
 678  				return (rc);
 679  		}
 680  		/* Provides/requires */
 681  		rc = pkg_jobs_universe_process_shlibs(universe, pkg);
 682  		if (rc != EPKG_OK)
 683  			return (rc);
 684  		rc = pkg_jobs_universe_process_provides_requires(universe, pkg);
 685  		if (rc != EPKG_OK)
 686  			return (rc);
 687  		break;
 688  	case PKG_JOBS_AUTOREMOVE:
 689  		rc = pkg_jobs_universe_process_deps(universe, pkg, flags);
 690  		if (rc != EPKG_OK)
 691  			return (rc);
 692  		rc = pkg_jobs_universe_process_shlibs(universe, pkg);
 693  		if (rc != EPKG_OK)
 694  			return (rc);
 695  		rc = pkg_jobs_universe_process_provides_requires(universe, pkg);
 696  		if (rc != EPKG_OK)
 697  			return (rc);
 698  		break;
 699  		/* XXX */
 700  		break;
 701  	case PKG_JOBS_DEINSTALL:
 702  		/* For delete jobs we worry only about local reverse deps */
 703  		flags |= DEPS_FLAG_REVERSE|DEPS_FLAG_FORCE_LOCAL;
 704  		if (job_flags & PKG_FLAG_RECURSIVE) {
 705  			rc = pkg_jobs_universe_process_deps(universe, pkg, flags);
 706  			if (rc != EPKG_OK)
 707  				return (rc);
 708  			rc = pkg_jobs_universe_process_shlibs(universe, pkg);
 709  			if (rc != EPKG_OK)
 710  				return (rc);
 711  			rc = pkg_jobs_universe_process_provides_requires(universe, pkg);
 712  			if (rc != EPKG_OK)
 713  				return (rc);
 714  			break;
 715  		}
 716  		break;
 717  	}
 718  
 719  	return (rc);
 720  }
 721  
 722  int
 723  pkg_jobs_universe_process(struct pkg_jobs_universe *universe,
 724  	struct pkg *pkg)
 725  {
 726  	return (pkg_jobs_universe_process_item(universe, pkg, NULL));
 727  }
 728  
 729  static void
 730  pkg_jobs_universe_provide_free(struct pkg_job_provide *pr)
 731  {
 732  	struct pkg_job_provide *cur, *tmp;
 733  
 734  	DL_FOREACH_SAFE(pr, cur, tmp) {
 735  		free (cur);
 736  	}
 737  }
 738  
 739  void
 740  pkg_jobs_universe_free(struct pkg_jobs_universe *universe)
 741  {
 742  	struct pkg_job_universe_item *cur, *curtmp;
 743  	pkghash_it it;
 744  
 745  	it = pkghash_iterator(universe->items);
 746  	while (pkghash_next(&it)) {
 747  		LL_FOREACH_SAFE(it.value, cur, curtmp) {
 748  			pkg_free(cur->pkg);
 749  			free(cur);
 750  		}
 751  	}
 752  	pkghash_destroy(universe->items);
 753  	universe->items = NULL;
 754  	pkghash_destroy(universe->seen);
 755  	universe->seen = NULL;
 756  	it = pkghash_iterator(universe->provides);
 757  	while (pkghash_next(&it))
 758  		pkg_jobs_universe_provide_free(it.value);
 759  	pkghash_destroy(universe->provides);
 760  	free(universe);
 761  }
 762  
 763  struct pkg_jobs_universe *
 764  pkg_jobs_universe_new(struct pkg_jobs *j)
 765  {
 766  	struct pkg_jobs_universe *universe;
 767  
 768  	universe = xcalloc(1, sizeof(struct pkg_jobs_universe));
 769  	universe->j = j;
 770  
 771  	return (universe);
 772  }
 773  
 774  struct pkg_job_universe_item *
 775  pkg_jobs_universe_find(struct pkg_jobs_universe *universe, const char *uid)
 776  {
 777  	return (pkghash_get_value(universe->items, uid));
 778  }
 779  
 780  static struct pkg_job_universe_item *
 781  pkg_jobs_universe_select_max_ver(struct pkg_job_universe_item *chain)
 782  {
 783  	struct pkg_job_universe_item *cur, *res = NULL;
 784  	bool found = false;
 785  	int r;
 786  
 787  	LL_FOREACH(chain, cur) {
 788  		if (cur->pkg->type == PKG_INSTALLED)
 789  			continue;
 790  
 791  		if (res != NULL) {
 792  			r = pkg_version_change_between(cur->pkg, res->pkg);
 793  			if (r == PKG_UPGRADE) {
 794  				res = cur;
 795  				found = true;
 796  			}
 797  			else if (r != PKG_REINSTALL) {
 798  				/*
 799  				 * Actually the selected package is newer than some other
 800  				 * packages in the chain
 801  				 */
 802  				found = true;
 803  			}
 804  		}
 805  		else {
 806  			res = cur;
 807  		}
 808  	}
 809  
 810  	return (found ? res : NULL);
 811  }
 812  
 813  static struct pkg_job_universe_item *
 814  pkg_jobs_universe_select_max_prio(struct pkg_job_universe_item *chain)
 815  {
 816  	struct pkg_repo *repo;
 817  	unsigned int max_pri = 0;
 818  	struct pkg_job_universe_item *cur, *res = NULL;
 819  
 820  	LL_FOREACH(chain, cur) {
 821  		if (cur->pkg->type == PKG_INSTALLED)
 822  			continue;
 823  
 824  		if (cur->pkg->reponame) {
 825  			repo = pkg_repo_find(cur->pkg->reponame);
 826  			if (repo && repo->priority > max_pri) {
 827  				res = cur;
 828  				max_pri = repo->priority;
 829  			}
 830  		}
 831  	}
 832  
 833  	return (res);
 834  }
 835  
 836  static struct pkg_job_universe_item *
 837  pkg_jobs_universe_select_same_repo(struct pkg_job_universe_item *chain,
 838  	struct pkg_job_universe_item *local, const char *assumed_reponame)
 839  {
 840  	struct pkg_repo *local_repo = NULL, *repo;
 841  	struct pkg_job_universe_item *cur, *res = NULL;
 842  
 843  	if (!local) {
 844  
 845  		if (assumed_reponame) {
 846  			local_repo = pkg_repo_find(assumed_reponame);
 847  		}
 848  	}
 849  	else {
 850  		if (local->pkg->reponame) {
 851  			local_repo = pkg_repo_find(local->pkg->reponame);
 852  		}
 853  		else {
 854  			const char *lrepo = pkg_kv_get(&local->pkg->annotations, "repository");
 855  			if (lrepo) {
 856  				local_repo = pkg_repo_find(lrepo);
 857  			}
 858  		}
 859  	}
 860  
 861  	if (local_repo == NULL) {
 862  		return (NULL);
 863  	}
 864  	else {
 865  		LL_FOREACH(chain, cur) {
 866  			if (cur->pkg->type == PKG_INSTALLED)
 867  				continue;
 868  
 869  			if (cur->pkg->reponame) {
 870  				repo = pkg_repo_find(cur->pkg->reponame);
 871  				if (repo == local_repo) {
 872  					res = cur;
 873  					break;
 874  				}
 875  			}
 876  		}
 877  	}
 878  
 879  	return (res);
 880  }
 881  
 882  struct pkg_job_universe_item *
 883  pkg_jobs_universe_select_candidate(struct pkg_job_universe_item *chain,
 884      struct pkg_job_universe_item *local, bool conservative,
 885      const char *reponame, bool pinning)
 886  {
 887  	struct pkg_job_universe_item *res = NULL;
 888  
 889  	if (local == NULL) {
 890  		/* New package selection */
 891  		if (conservative) {
 892  			/* Check same repo */
 893  			if (reponame && pinning) {
 894  				res =  pkg_jobs_universe_select_same_repo(chain, NULL, reponame);
 895  			}
 896  
 897  			if (res == NULL) {
 898  				/* Priority -> version */
 899  				res = pkg_jobs_universe_select_max_prio(chain);
 900  				if (res == NULL) {
 901  					res = pkg_jobs_universe_select_max_ver(chain);
 902  				}
 903  			}
 904  		}
 905  		else {
 906  			if (reponame && pinning) {
 907  				res =  pkg_jobs_universe_select_same_repo(chain, NULL, reponame);
 908  			}
 909  
 910  			if (res == NULL) {
 911  				/* Version -> priority */
 912  				res = pkg_jobs_universe_select_max_ver(chain);
 913  				if (res == NULL) {
 914  					res = pkg_jobs_universe_select_max_prio(chain);
 915  				}
 916  			}
 917  		}
 918  	}
 919  	else {
 920  		if (conservative) {
 921  			/* same -> prio -> version */
 922  			if (pinning)
 923  				res = pkg_jobs_universe_select_same_repo(chain, local, reponame);
 924  			if (res == NULL) {
 925  				res = pkg_jobs_universe_select_max_prio(chain);
 926  			}
 927  			if (res == NULL) {
 928  				res = pkg_jobs_universe_select_max_ver(chain);
 929  			}
 930  		}
 931  		else {
 932  			/* same -> version -> prio */
 933  			if (pinning)
 934  				res = pkg_jobs_universe_select_same_repo(chain, local, reponame);
 935  			if (res == NULL) {
 936  				res = pkg_jobs_universe_select_max_ver(chain);
 937  			}
 938  			if (res == NULL) {
 939  				res = pkg_jobs_universe_select_max_prio(chain);
 940  			}
 941  		}
 942  	}
 943  
 944  	/*
 945  	 * When all heuristics fail and a local package exists, prefer
 946  	 * the remote candidate whose digest matches the installed one.
 947  	 * This avoids proposing a spurious reinstall from another repo
 948  	 * when the same version is available in multiple repositories.
 949  	 */
 950  	if (res == NULL && local != NULL) {
 951  		struct pkg_job_universe_item *cur;
 952  		LL_FOREACH(chain, cur) {
 953  			if (cur->pkg->type != PKG_INSTALLED &&
 954  			    STREQ(local->pkg->digest, cur->pkg->digest)) {
 955  				res = cur;
 956  				break;
 957  			}
 958  		}
 959  	}
 960  
 961  	/* Fallback to any */
 962  	return (res != NULL ? res : chain);
 963  }
 964  
 965  void
 966  pkg_jobs_universe_process_upgrade_chains(struct pkg_jobs *j)
 967  {
 968  	struct pkg_job_universe_item *unit, *cur, *local;
 969  	struct pkg_job_request *req;
 970  	struct pkg_job_request_item *rit, *rtmp;
 971  	pkghash_it it;
 972  
 973  	it = pkghash_iterator(j->universe->items);
 974  	while (pkghash_next(&it)) {
 975  		unsigned vercnt = 0;
 976  		unit = (struct pkg_job_universe_item *)it.value;
 977  
 978  		req = pkghash_get_value(j->request_add, unit->pkg->uid);
 979  		if (req == NULL) {
 980  			/* Not obviously requested */
 981  			continue;
 982  		}
 983  
 984  		local = NULL;
 985  		LL_FOREACH(unit, cur) {
 986  			if (cur->pkg->type == PKG_INSTALLED)
 987  				local = cur;
 988  			vercnt ++;
 989  		}
 990  
 991  		if (local != NULL && local->pkg->locked) {
 992  			dbg(1, "removing %s from the request as it is locked",
 993  				local->pkg->uid);
 994  			pkghash_del(j->request_add, req->item->pkg->uid);
 995  			pkg_jobs_request_free(req);
 996  			continue;
 997  		}
 998  
 999  		if (vercnt <= 1)
1000  			continue;
1001  
1002  		/*
1003  		 * Here we have more than one upgrade candidate,
1004  		 * if local == NULL, then we have two remote repos,
1005  		 * if local != NULL, then we have unspecified upgrade path
1006  		 */
1007  
1008  		if ((local == NULL && vercnt > 1) || (vercnt > 2)) {
1009  			/* Select the most recent or one of packages */
1010  			struct pkg_job_universe_item *selected;
1011  
1012  			selected = pkg_jobs_universe_select_candidate(unit, local,
1013  				j->conservative, NULL, j->pinning);
1014  			/*
1015  			 * Now remove all requests but selected from the requested
1016  			 * candidates
1017  			 */
1018  			assert(selected != NULL);
1019  			pkghash_del(j->request_add, req->item->pkg->uid);
1020  
1021  			/*
1022  			 * We also check if the selected package has different digest,
1023  			 * and if it has the same digest we proceed only if we have a
1024  			 * forced job
1025  			 */
1026  			if (local != NULL && STREQ(local->pkg->digest, selected->pkg->digest) &&
1027  				(j->flags & PKG_FLAG_FORCE) == 0) {
1028  				dbg(1, "removing %s from the request as it is the "
1029  								"same as local", selected->pkg->uid);
1030  				continue;
1031  			}
1032  
1033  			LL_FOREACH(unit, cur) {
1034  				if (cur == selected)
1035  					continue;
1036  
1037  				DL_FOREACH_SAFE(req->item, rit, rtmp) {
1038  					if (rit->unit == cur) {
1039  						DL_DELETE(req->item, rit);
1040  						free(rit);
1041  					}
1042  				}
1043  			}
1044  			if (req->item == NULL) {
1045  				rit = xcalloc(1, sizeof(*rit));
1046  				rit->pkg = selected->pkg;
1047  				rit->unit = selected;
1048  				DL_APPEND(req->item, rit);
1049  			}
1050  			pkghash_safe_add(j->request_add, selected->pkg->uid, req, NULL);
1051  		}
1052  	}
1053  }
1054  
1055  struct pkg_job_universe_item*
1056  pkg_jobs_universe_get_upgrade_candidates(struct pkg_jobs_universe *universe,
1057  	const char *uid, struct pkg *lp, bool force, const char *version)
1058  {
1059  	struct pkg *pkg = NULL, *selected = lp;
1060  	struct pkgdb_it *it;
1061  	struct pkg_job_universe_item *unit, *ucur;
1062  	int flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_OPTIONS|
1063  					PKG_LOAD_REQUIRES|PKG_LOAD_PROVIDES|
1064  					PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
1065  					PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
1066  	pkgs_t candidates = vec_init();
1067  
1068  	unit = pkghash_get_value(universe->items, uid);
1069  	if (unit != NULL) {
1070  		/*
1071  		 * If a unit has been found, we have already found the potential
1072  		 * upgrade chain for it
1073  		 */
1074  		if (force) {
1075  			/*
1076  			 * We also need to ensure that a chain contains remote packages
1077  			 * in case of forced upgrade
1078  			 */
1079  			DL_FOREACH(unit, ucur) {
1080  				if (ucur->pkg->type != PKG_INSTALLED) {
1081  					return (unit);
1082  				}
1083  			}
1084  		}
1085  		else {
1086  			return (unit);
1087  		}
1088  	}
1089  
1090  	if ((it = pkgdb_repo_query2(universe->j->db, uid, MATCH_INTERNAL,
1091  		universe->j->reponames)) == NULL)
1092  		return (NULL);
1093  
1094  	while (pkgdb_it_next(it, &pkg, flag) == EPKG_OK) {
1095  
1096  		if (version != NULL && strcmp(pkg->version, version) != 0)
1097  			continue;
1098  
1099  		if (force) {
1100  			/* Just add everything */
1101  			selected = pkg;
1102  		}
1103  		else {
1104  			if (selected == lp &&
1105  					(lp == NULL || pkg_jobs_need_upgrade(&universe->j->system_shlibs, pkg, lp)))
1106  				selected = pkg;
1107  			else if (pkg_version_change_between(pkg, selected) == PKG_UPGRADE)
1108  				selected = pkg;
1109  		}
1110  		vec_push(&candidates, pkg);
1111  		pkg = NULL;
1112  	}
1113  
1114  	pkgdb_it_free(it);
1115  
1116  	if (lp != NULL) {
1117  		/* Add local package to the universe as well */
1118  		pkg_jobs_universe_add_pkg(universe, lp, NULL);
1119  	}
1120  	if (selected != lp) {
1121  		/* We need to add the whole chain of upgrade candidates */
1122  		vec_rforeach(candidates, i) {
1123  			pkg_jobs_universe_add_pkg(universe, candidates.d[i],
1124  			    NULL);
1125  		}
1126  	}
1127  	else {
1128  		vec_free_and_free(&candidates, pkg_free);
1129  		return (NULL);
1130  	}
1131  
1132  	unit = pkghash_get_value(universe->items, uid);
1133  	vec_free(&candidates);
1134  
1135  	return (unit);
1136  }