xref: /openbsd-src/usr.sbin/pkg_add/OpenBSD/Update.pm (revision 35d93a1a7bfe54d449f6fb4d7a0af3e3430185f6)
138727c08Sespie# ex:ts=8 sw=4:
2*35d93a1aSespie# $OpenBSD: Update.pm,v 1.171 2023/10/07 09:10:03 espie Exp $
338727c08Sespie#
445019a4aSespie# Copyright (c) 2004-2014 Marc Espie <espie@openbsd.org>
538727c08Sespie#
638727c08Sespie# Permission to use, copy, modify, and distribute this software for any
738727c08Sespie# purpose with or without fee is hereby granted, provided that the above
838727c08Sespie# copyright notice and this permission notice appear in all copies.
938727c08Sespie#
1038727c08Sespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1138727c08Sespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1238727c08Sespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1338727c08Sespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1438727c08Sespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1538727c08Sespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
160fbefeddSespie
17039cbdaaSespieuse v5.36;
1838727c08Sespie
19e05ce761Sespiepackage OpenBSD::Handle;
20039cbdaaSespiesub update($self, $updater, $set, $state)
21e05ce761Sespie{
22e05ce761Sespie
23e05ce761Sespie	return $updater->process_handle($set, $self, $state);
24e05ce761Sespie}
25e05ce761Sespie
26d47afce4Sespie# TODO hint and hint2 are horrible names
27e05ce761Sespiepackage OpenBSD::hint;
28039cbdaaSespiesub update($self, $updater, $set, $state)
29e05ce761Sespie{
30e05ce761Sespie
31e05ce761Sespie	return $updater->process_hint($set, $self, $state);
32e05ce761Sespie}
33e05ce761Sespie
34e05ce761Sespiepackage OpenBSD::hint2;
35039cbdaaSespiesub update($self, $updater, $set, $state)
36e05ce761Sespie{
37e05ce761Sespie	return $updater->process_hint2($set, $self, $state);
38e05ce761Sespie}
39e05ce761Sespie
4038727c08Sespiepackage OpenBSD::Update;
41646f71ecSespieuse OpenBSD::PackageInfo;
42646f71ecSespieuse OpenBSD::PackageName;
437ba739cfSbernduse OpenBSD::Error;
44eb5eab35Sespieuse OpenBSD::UpdateSet;
4538727c08Sespie
46039cbdaaSespiesub new($class)
4738727c08Sespie{
48eb5eab35Sespie	return bless {}, $class;
4959ccc3c8Sespie}
5059ccc3c8Sespie
51039cbdaaSespiesub add_handle($self, $set, $old, $n)
52e05ce761Sespie{
5328dfaf57Sespie	$old->{update_found} = $n;
54e05ce761Sespie	$set->add_newer($n);
55e05ce761Sespie}
56e05ce761Sespie
57039cbdaaSespiesub add_location($self, $set, $handle, $location)
5859ccc3c8Sespie{
59e05ce761Sespie	$self->add_handle($set, $handle,
60e05ce761Sespie	    OpenBSD::Handle->from_location($location));
6159ccc3c8Sespie}
6259ccc3c8Sespie
63039cbdaaSespiesub look_for_debug($self, $set, $oldname, $newname, $state)
6492bd379cSespie{
6592bd379cSespie	# hurdles to pass before adding debug packages
6692bd379cSespie	return unless $state->{debug_packages};
6792bd379cSespie
6892bd379cSespie	return if $state->tracker->is_to_update("debug-".$oldname);
6992bd379cSespie	my $dbg = "debug-".$newname;
7092bd379cSespie	my $l = $set->match_locations(OpenBSD::Search::Exact->new($dbg));
7192bd379cSespie	# TODO if @$l == 0, I should look for other packages with similar names
7292bd379cSespie	# just so I can warn for out-of-date/shearing in the mirrors.
7392bd379cSespie	return if @$l != 1;
7492bd379cSespie	$set->add_newer(OpenBSD::Handle->from_location($l->[0]));
7592bd379cSespie}
7692bd379cSespie
77039cbdaaSespiesub found_update($self, $set, $old, $location, $state)
7892bd379cSespie{
7992bd379cSespie	$self->add_location($set, $old, $location);
8092bd379cSespie	$self->look_for_debug($set, $old->pkgname, $location->name, $state);
8192bd379cSespie}
8292bd379cSespie
83039cbdaaSespiesub progress_message($self, $state, @r)
84b7510ac8Sespie{
854b4be44cSespie	my $msg = $state->f(@r);
862d4feb81Sespie	$msg .= $state->ntogo_string;
87b7510ac8Sespie	$state->progress->message($msg);
88e74b30b0Sespie	$state->say($msg) if $state->verbose >= 2;
89b7510ac8Sespie}
90b7510ac8Sespie
91039cbdaaSespiesub process_handle($self, $set, $h, $state)
9259ccc3c8Sespie{
93cc96d067Sespie	my $pkgname = $h->pkgname;
9459ccc3c8Sespie
95c9826cdbSespie	if ($pkgname =~ m/^\.libs\d*\-/o) {
96eb5eab35Sespie		return 0;
971548337dSespie	}
98bb4ec1ceSespie
996fe055f5Sespie	if (!$set->{quirks}) {
10042cea554Sespie		my $base = 0;
101875cde4bSespie		$state->run_quirks(
102039cbdaaSespie		    sub($quirks) {
103875cde4bSespie			$base = $quirks->is_base_system($h, $state);
104875cde4bSespie		    });
10542cea554Sespie		if ($base) {
10642cea554Sespie			$h->{update_found} = OpenBSD::Handle->system;
10742cea554Sespie			$set->{updates}++;
10842cea554Sespie			return 1;
10942cea554Sespie		}
1106fe055f5Sespie	}
111f5729cc1Sespie
112bb4ec1ceSespie	my $plist = OpenBSD::PackingList->from_installation($pkgname,
113bb4ec1ceSespie	    \&OpenBSD::PackingList::UpdateInfoOnly);
114bb4ec1ceSespie	if (!defined $plist) {
1157e83eca3Sespie		$state->fatal("can't locate #1", $pkgname);
116bb4ec1ceSespie	}
117bb4ec1ceSespie
118afe6de13Sespie	if ($plist->has('firmware') && !$state->defines('FW_UPDATE')) {
119d2decf4fSespie		$set->move_kept($h);
120*35d93a1aSespie		$h->{is_firmware} = 1;
121d2decf4fSespie		return 0;
122d2decf4fSespie	}
123d2decf4fSespie
124d2decf4fSespie#	if (defined $plist->{url}) {
125d2decf4fSespie#		my $repo;
12601734cf0Sespie#		($repo, undef) = $state->repo->path_parse($plist->{url}->name);
127d2decf4fSespie#		$set->add_repositories($repo);
128d2decf4fSespie#	}
1296f68aac1Sespie	my @search = ();
130fdd1f733Sespie
131fdd1f733Sespie	my $sname = $pkgname;
132fdd1f733Sespie	while ($sname =~ s/^partial\-//o) {
133fdd1f733Sespie	}
134fdd1f733Sespie	push(@search, OpenBSD::Search::Stem->split($sname));
1350fe785c3Sespie
1366fe055f5Sespie	if (!$set->{quirks}) {
137875cde4bSespie		$state->run_quirks(
138039cbdaaSespie		    sub($quirks) {
139875cde4bSespie			$quirks->tweak_search(\@search, $h, $state);
140875cde4bSespie		    });
1416fe055f5Sespie	}
14224dd5be2Sespie	my $oldfound = 0;
1432f37e678Sjeremy	my @skipped_locs = ();
1445b0d5555Sespie
1455bd96a97Sespie	# XXX this is nasty: maybe we added an old set to update
1465bd96a97Sespie	# because of conflicts, in which case the pkgpath +
1475bd96a97Sespie	# conflict should be enough  to "match".
1485bd96a97Sespie	for my $n ($set->newer) {
149d7c67ec8Sespie		if (($state->{hard_replace} ||
150d7c67ec8Sespie		    $n->location->update_info->match_pkgpath($plist)) &&
15146373398Sespie			$n->conflict_list->conflicts_with($sname)) {
1525bd96a97Sespie				$self->add_handle($set, $h, $n);
1535bd96a97Sespie				return 1;
1545bd96a97Sespie		}
1555bd96a97Sespie	}
156fb5688e8Sespie	# XXX all that code conveniently forgets about old versions, while
157fb5688e8Sespie	# marking them as "normal".
1583a50f0a9Sjmc	# there should be some error path when we consistently fail to find
159fb5688e8Sespie	# an equal-or-newer version in our repository, so that pkg_add has
160fb5688e8Sespie	# consistent exit codes.
161a409537dSespie	if (!$state->defines('downgrade')) {
162fdd1f733Sespie		push(@search, OpenBSD::Search::FilterLocation->more_recent_than($sname, \$oldfound));
163b6a296a5Sespie	}
164e451b54dSespie	push(@search, OpenBSD::Search::FilterLocation->new(
165039cbdaaSespie	    sub($l) {
166e451b54dSespie		if (@$l == 0) {
167e451b54dSespie			return $l;
16825f8e080Sespie		}
1695b0d5555Sespie		my @l2 = ();
170fc9f0195Sespie		for my $loc (@$l) {
171fc9f0195Sespie		    if (!$loc) {
172646f71ecSespie			    next;
173646f71ecSespie		    }
174fc9f0195Sespie		    my $p2 = $loc->update_info;
175646f71ecSespie		    if (!$p2) {
176646f71ecSespie			next;
177646f71ecSespie		    }
178646f71ecSespie		    if ($p2->has('arch')) {
179646f71ecSespie			unless ($p2->{arch}->check($state->{arch})) {
1803ecf5ca9Sespie			    $loc->forget;
181646f71ecSespie			    next;
182646f71ecSespie			}
183646f71ecSespie		    }
1845004679dSespie		    if (!$plist->match_pkgpath($p2)) {
1852f37e678Sjeremy			push(@skipped_locs, $loc);
1865004679dSespie			next
1875004679dSespie		    }
1888b437e75Sespie		    my $r = $plist->signature->compare($p2->signature, $state);
189a409537dSespie		    if (defined $r && $r > 0 && !$state->defines('downgrade')) {
190f11e17d2Sespie		    	$oldfound = 1;
1913ecf5ca9Sespie			$loc->forget;
1925b0d5555Sespie			next;
193646f71ecSespie		    }
194fc9f0195Sespie		    push(@l2, $loc);
195646f71ecSespie		}
196e451b54dSespie		return \@l2;
1976f68aac1Sespie	    }));
198646f71ecSespie
199a409537dSespie	if (!$state->defines('allversions')) {
200e451b54dSespie		push(@search, OpenBSD::Search::FilterLocation->keep_most_recent);
201e451b54dSespie	}
2025b0d5555Sespie
20388e41ea0Sespie	my $l = $set->match_locations(@search);
2042f37e678Sjeremy
2052f37e678Sjeremy	for my $loc (@skipped_locs) {
2062f37e678Sjeremy		if (@$l == 0 && $state->verbose) {
2072f37e678Sjeremy			$self->say_skipped_packages($state, $plist,
2082f37e678Sjeremy				$loc->update_info);
2092f37e678Sjeremy		}
2102f37e678Sjeremy		$loc->forget;
2112f37e678Sjeremy	}
2122f37e678Sjeremy
213e451b54dSespie	if (@$l == 0) {
21424dd5be2Sespie		if ($oldfound) {
2152172b28bSespie			$set->move_kept($h);
216b7510ac8Sespie			$self->progress_message($state,
2174b4be44cSespie			    "No need to update #1", $pkgname);
21892bd379cSespie			$self->look_for_debug($set, $pkgname, $pkgname, $state);
21924dd5be2Sespie			return 0;
22024dd5be2Sespie		}
221eb5eab35Sespie		return undef;
2225b0d5555Sespie	}
2232d4feb81Sespie	$state->say("Update candidates: #1 -> #2#3", $pkgname,
2242d4feb81Sespie	    join(' ', map {$_->name} @$l), $state->ntogo_string)
2252d4feb81Sespie		if $state->verbose;
226646f71ecSespie
22781dcf8b8Sespie	my $r = $state->choose_location($pkgname, $l);
22881dcf8b8Sespie	if (defined $r) {
22992bd379cSespie		$self->found_update($set, $h, $r, $state);
230eb5eab35Sespie		return 1;
231646f71ecSespie	} else {
232646f71ecSespie		$state->{issues} = 1;
233eb5eab35Sespie		return undef;
23495cbd9edSespie	}
23595cbd9edSespie}
236023fe338Sespie
237039cbdaaSespiesub say_skipped_packages($self, $state, $o, $n)
2382f37e678Sjeremy{
2392f37e678Sjeremy	my $o_name = $o->pkgname;
2402f37e678Sjeremy	my @o_ps = map { @{$o->pkgpath->{$_}} } keys %{$o->pkgpath};
2412f37e678Sjeremy	my $o_pp = join(" ", map {$_->fullpkgpath} @o_ps);
2422f37e678Sjeremy
2432f37e678Sjeremy	my $n_name = $n->pkgname;
2442f37e678Sjeremy	my @n_ps = map { @{$n->pkgpath->{$_}} } keys %{$n->pkgpath};
2452f37e678Sjeremy	my $n_pp= join(" ", map {$_->fullpkgpath} @n_ps);
2462f37e678Sjeremy
2472f37e678Sjeremy	my $t  = "Skipping #1 (update candidate for #2)";
2482f37e678Sjeremy	   $t .= "\n\t#2 pkgpaths: #4\n\t#1 pkgpaths: #3";
2492f37e678Sjeremy
2502f37e678Sjeremy	$state->say($t, $n_name, $o_name, $n_pp, $o_pp);
2512f37e678Sjeremy}
2522f37e678Sjeremy
253039cbdaaSespiesub find_nearest($base, $locs)
254612a857cSespie{
255612a857cSespie	my $pkgname = OpenBSD::PackageName->from_string($base);
256612a857cSespie	return undef if !defined $pkgname->{version};
257612a857cSespie	my @sorted = sort {$a->pkgname->{version}->compare($b->pkgname->{version}) } @$locs;
258612a857cSespie	if ($sorted[0]->pkgname->{version}->compare($pkgname->{version}) > 0) {
259612a857cSespie		return $sorted[0];
260612a857cSespie	}
261612a857cSespie	if ($sorted[-1]->pkgname->{version}->compare($pkgname->{version}) < 0) {
262612a857cSespie		return $sorted[-1];
263612a857cSespie	}
264612a857cSespie	return undef;
265612a857cSespie}
266612a857cSespie
267039cbdaaSespiesub process_hint($self, $set, $hint, $state)
2687d63c3f8Sespie{
2697d63c3f8Sespie	my $l;
27042caadbcSespie	my $hint_name = $hint->pkgname;
2717d63c3f8Sespie	my $k = OpenBSD::Search::FilterLocation->keep_most_recent;
2727d63c3f8Sespie	# first try to find us exactly
2737d63c3f8Sespie
2744b4be44cSespie	$self->progress_message($state, "Looking for #1", $hint_name);
27588e41ea0Sespie	$l = $set->match_locations(OpenBSD::Search::Exact->new($hint_name), $k);
2767d63c3f8Sespie	if (@$l == 0) {
27742caadbcSespie		my $t = $hint_name;
2787d63c3f8Sespie		$t =~ s/\-\d([^-]*)\-?/--/;
2798b6f798cSespie		my @search = (OpenBSD::Search::Stem->new($t));
2808b6f798cSespie		$state->run_quirks(
281039cbdaaSespie		    sub($quirks) {
2828b6f798cSespie			$quirks->tweak_search(\@search, $hint, $state);
2838b6f798cSespie		    });
2848b6f798cSespie		$l = $set->match_locations(@search, $k);
2857d63c3f8Sespie	}
286612a857cSespie	if (@$l > 1) {
287612a857cSespie		my $r = find_nearest($hint_name, $l);
288612a857cSespie		if (defined $r) {
28992bd379cSespie			$self->found_update($set, $hint, $r, $state);
290612a857cSespie			return 1;
291612a857cSespie		}
292612a857cSespie	}
29342caadbcSespie	my $r = $state->choose_location($hint_name, $l);
2947d63c3f8Sespie	if (defined $r) {
29592bd379cSespie		$self->found_update($set, $hint, $r, $state);
2967d4e60deSespie		OpenBSD::Add::tag_user_packages($set);
2977d63c3f8Sespie		return 1;
2987d63c3f8Sespie	} else {
2997d63c3f8Sespie		return 0;
3007d63c3f8Sespie	}
3017d63c3f8Sespie}
3027d63c3f8Sespie
303e05ce761Sespiemy $cache = {};
304e05ce761Sespie
305039cbdaaSespiesub process_hint2($self, $set, $hint, $state)
306e05ce761Sespie{
307e05ce761Sespie	my $pkgname = $hint->pkgname;
308396f4403Sespie	my $pkg2;
3093eadd976Sespie	if ($pkgname =~ m/[\/\:]/o) {
31088e41ea0Sespie		my $repo;
311396f4403Sespie		($repo, $pkg2) = $state->repo->path_parse($pkgname);
31288e41ea0Sespie		$set->add_repositories($repo);
313396f4403Sespie	} else {
314396f4403Sespie		$pkg2 = $pkgname;
315396f4403Sespie	}
316396f4403Sespie	if (OpenBSD::PackageName::is_stem($pkg2)) {
317396f4403Sespie		my $l = $state->updater->stem2location($set, $pkg2, $state,
318f5729cc1Sespie		    $set->{quirks});
319e05ce761Sespie		if (defined $l) {
3202338bd65Sespie			$self->add_location($set, $hint, $l);
321e9bfaee7Sespie			$self->look_for_debug($set, $l->name, $l->name, $state);
322e05ce761Sespie		} else {
323e05ce761Sespie			return undef;
324e05ce761Sespie		}
325e05ce761Sespie	} else {
326e05ce761Sespie		if (!defined $cache->{$pkgname}) {
327e05ce761Sespie			$self->add_handle($set, $hint, OpenBSD::Handle->create_new($pkgname));
328e05ce761Sespie			$cache->{$pkgname} = 1;
329396f4403Sespie			$pkg2 =~ s/\.tgz$//;
330396f4403Sespie			$self->look_for_debug($set, $pkg2, $pkg2, $state);
331e05ce761Sespie		}
332e05ce761Sespie	}
333e05ce761Sespie	OpenBSD::Add::tag_user_packages($set);
334e05ce761Sespie	return 1;
335e05ce761Sespie}
336e05ce761Sespie
337039cbdaaSespiesub process_set($self, $set, $state)
338f1088463Sespie{
33998b10e60Sespie	my @problems = ();
340e05ce761Sespie	for my $h ($set->older, $set->hints) {
34128dfaf57Sespie		next if $h->{update_found};
3425bd96a97Sespie		if (!defined $h->update($self, $set, $state)) {
34398b10e60Sespie			push(@problems, $h->pkgname);
344f1088463Sespie		}
345f1088463Sespie	}
34698b10e60Sespie	if (@problems > 0) {
3477dd5c23bSespie		$state->tracker->cant($set) if !$set->{quirks};
348b7510ac8Sespie		if ($set->{updates} != 0) {
349861c3ddfSespie			$state->say("Can't update #1: no update found for #2",
3507e83eca3Sespie			    $set->print, join(',', @problems));
351b7510ac8Sespie		}
352f1088463Sespie		return 0;
3530849d6feSespie	} elsif ($set->{updates} == 0) {
3547dd5c23bSespie		$state->tracker->uptodate($set);
355f1088463Sespie		return 0;
356f1088463Sespie	}
357e05ce761Sespie	$state->tracker->add_set($set);
358f1088463Sespie	return 1;
359f1088463Sespie}
360f1088463Sespie
361039cbdaaSespiesub stem2location($self, $locator, $name, $state, $is_quirks = 0)
362e05ce761Sespie{
36388e41ea0Sespie	my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name));
364a409537dSespie	if (@$l > 1 && !$state->defines('allversions')) {
365e05ce761Sespie		$l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l);
366e05ce761Sespie	}
367f5729cc1Sespie	return $state->choose_location($name, $l, $is_quirks);
368e05ce761Sespie}
369e05ce761Sespie
37038727c08Sespie1;
371