xref: /openbsd-src/usr.sbin/pkg_add/OpenBSD/Update.pm (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1# ex:ts=8 sw=4:
2# $OpenBSD: Update.pm,v 1.162 2015/01/04 14:20:04 espie Exp $
3#
4# Copyright (c) 2004-2014 Marc Espie <espie@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
17use strict;
18use warnings;
19
20package OpenBSD::Handle;
21sub update
22{
23	my ($self, $updater, $set, $state) = @_;
24
25	return $updater->process_handle($set, $self, $state);
26}
27
28package OpenBSD::hint;
29sub update
30{
31	my ($self, $updater, $set, $state) = @_;
32
33	return $updater->process_hint($set, $self, $state);
34}
35
36package OpenBSD::hint2;
37sub update
38{
39	my ($self, $updater, $set, $state) = @_;
40
41	return $updater->process_hint2($set, $self, $state);
42}
43
44package OpenBSD::Update;
45use OpenBSD::PackageInfo;
46use OpenBSD::PackageName;
47use OpenBSD::Error;
48use OpenBSD::UpdateSet;
49
50sub new
51{
52	my $class = shift;
53	return bless {}, $class;
54}
55
56sub add_handle
57{
58	my ($self, $set, $old, $n) = @_;
59	$old->{update_found} = $n;
60	$set->add_newer($n);
61}
62
63sub add_location
64{
65	my ($self, $set, $handle, $location) = @_;
66
67	$self->add_handle($set, $handle,
68	    OpenBSD::Handle->from_location($location));
69}
70
71sub progress_message
72{
73	my ($self, $state, @r) = @_;
74	my $msg = $state->f(@r);
75	$msg .= $state->ntogo_string;
76	$state->progress->message($msg);
77	$state->say($msg) if $state->verbose >= 2;
78}
79
80sub process_handle
81{
82	my ($self, $set, $h, $state) = @_;
83	my $pkgname = $h->pkgname;
84
85	if ($pkgname =~ m/^\.libs\d*\-/o) {
86		return 0;
87	}
88
89	if (!$set->{quirks}) {
90		my $base = 0;
91		$state->run_quirks(
92		    sub {
93			my $quirks = shift;
94			$base = $quirks->is_base_system($h, $state);
95		    });
96		if ($base) {
97			$h->{update_found} = OpenBSD::Handle->system;
98			$set->{updates}++;
99			return 1;
100		}
101	}
102
103	my $plist = OpenBSD::PackingList->from_installation($pkgname,
104	    \&OpenBSD::PackingList::UpdateInfoOnly);
105	if (!defined $plist) {
106		$state->fatal("can't locate #1", $pkgname);
107	}
108
109	if ($plist->has('firmware') && !$state->defines('FW_UPDATE')) {
110		$set->move_kept($h);
111		return 0;
112	}
113
114#	if (defined $plist->{url}) {
115#		my $repo;
116#		($repo, undef) = $state->repo->path_parse($plist->{url}->name);
117#		$set->add_repositories($repo);
118#	}
119	my @search = ();
120
121	my $sname = $pkgname;
122	while ($sname =~ s/^partial\-//o) {
123	}
124	push(@search, OpenBSD::Search::Stem->split($sname));
125
126	if (!$set->{quirks}) {
127		$state->run_quirks(
128		    sub {
129			my $quirks = shift;
130			$quirks->tweak_search(\@search, $h, $state);
131		    });
132	}
133	my $oldfound = 0;
134	my @skipped_locs = ();
135
136	# XXX this is nasty: maybe we added an old set to update
137	# because of conflicts, in which case the pkgpath +
138	# conflict should be enough  to "match".
139	for my $n ($set->newer) {
140		if (($state->{hard_replace} ||
141		    $n->location->update_info->match_pkgpath($plist)) &&
142			$n->conflict_list->conflicts_with($sname)) {
143				$self->add_handle($set, $h, $n);
144				return 1;
145		}
146	}
147	if (!$state->defines('downgrade')) {
148		push(@search, OpenBSD::Search::FilterLocation->more_recent_than($sname, \$oldfound));
149	}
150	push(@search, OpenBSD::Search::FilterLocation->new(
151	    sub {
152		my $l = shift;
153		if (@$l == 0) {
154			return $l;
155		}
156		my @l2 = ();
157		for my $loc (@$l) {
158		    if (!$loc) {
159			    next;
160		    }
161		    my $p2 = $loc->update_info;
162		    if (!$p2) {
163			next;
164		    }
165		    if ($p2->has('arch')) {
166			unless ($p2->{arch}->check($state->{arch})) {
167			    $loc->forget;
168			    next;
169			}
170		    }
171		    if (!$plist->match_pkgpath($p2)) {
172			push(@skipped_locs, $loc);
173			next
174		    }
175		    my $r = $plist->signature->compare($p2->signature);
176		    if (defined $r && $r > 0 && !$state->defines('downgrade')) {
177		    	$oldfound = 1;
178			$loc->forget;
179			next;
180		    }
181		    push(@l2, $loc);
182		}
183		return \@l2;
184	    }));
185
186	if (!$state->defines('allversions')) {
187		push(@search, OpenBSD::Search::FilterLocation->keep_most_recent);
188	}
189
190	my $l = $set->match_locations(@search);
191
192	for my $loc (@skipped_locs) {
193		if (@$l == 0 && $state->verbose) {
194			$self->say_skipped_packages($state, $plist,
195				$loc->update_info);
196		}
197		$loc->forget;
198	}
199
200	if (@$l == 0) {
201		if ($oldfound) {
202			$set->move_kept($h);
203			$self->progress_message($state,
204			    "No need to update #1", $pkgname);
205
206			return 0;
207		}
208		return undef;
209	}
210	$state->say("Update candidates: #1 -> #2#3", $pkgname,
211	    join(' ', map {$_->name} @$l), $state->ntogo_string)
212		if $state->verbose;
213
214	my $r = $state->choose_location($pkgname, $l);
215	if (defined $r) {
216		$self->add_location($set, $h, $r);
217		return 1;
218	} else {
219		$state->{issues} = 1;
220		return undef;
221	}
222}
223
224sub say_skipped_packages
225{
226	my ($self, $state, $o, $n) = @_;
227
228	my $o_name = $o->pkgname;
229	my @o_ps = map { @{$o->pkgpath->{$_}} } keys %{$o->pkgpath};
230	my $o_pp = join(" ", map {$_->fullpkgpath} @o_ps);
231
232	my $n_name = $n->pkgname;
233	my @n_ps = map { @{$n->pkgpath->{$_}} } keys %{$n->pkgpath};
234	my $n_pp= join(" ", map {$_->fullpkgpath} @n_ps);
235
236	my $t  = "Skipping #1 (update candidate for #2)";
237	   $t .= "\n\t#2 pkgpaths: #4\n\t#1 pkgpaths: #3";
238
239	$state->say($t, $n_name, $o_name, $n_pp, $o_pp);
240}
241
242sub find_nearest
243{
244	my ($base, $locs) = @_;
245
246	my $pkgname = OpenBSD::PackageName->from_string($base);
247	return undef if !defined $pkgname->{version};
248	my @sorted = sort {$a->pkgname->{version}->compare($b->pkgname->{version}) } @$locs;
249	if ($sorted[0]->pkgname->{version}->compare($pkgname->{version}) > 0) {
250		return $sorted[0];
251	}
252	if ($sorted[-1]->pkgname->{version}->compare($pkgname->{version}) < 0) {
253		return $sorted[-1];
254	}
255	return undef;
256}
257
258sub process_hint
259{
260	my ($self, $set, $hint, $state) = @_;
261
262	my $l;
263	my $hint_name = $hint->pkgname;
264	my $k = OpenBSD::Search::FilterLocation->keep_most_recent;
265	# first try to find us exactly
266
267	$self->progress_message($state, "Looking for #1", $hint_name);
268	$l = $set->match_locations(OpenBSD::Search::Exact->new($hint_name), $k);
269	if (@$l == 0) {
270		my $t = $hint_name;
271		$t =~ s/\-\d([^-]*)\-?/--/;
272		my @search = (OpenBSD::Search::Stem->new($t));
273		$state->run_quirks(
274		    sub {
275			my $quirks = shift;
276			$quirks->tweak_search(\@search, $hint, $state);
277		    });
278		$l = $set->match_locations(@search, $k);
279	}
280	if (@$l > 1) {
281		my $r = find_nearest($hint_name, $l);
282		if (defined $r) {
283			$self->add_location($set, $hint, $r);
284			return 1;
285		}
286	}
287	my $r = $state->choose_location($hint_name, $l);
288	if (defined $r) {
289		$self->add_location($set, $hint, $r);
290		OpenBSD::Add::tag_user_packages($set);
291		return 1;
292	} else {
293		return 0;
294	}
295}
296
297my $cache = {};
298
299sub process_hint2
300{
301	my ($self, $set, $hint, $state) = @_;
302	my $pkgname = $hint->pkgname;
303	if (OpenBSD::PackageName::is_stem($pkgname)) {
304		if ($pkgname =~ m/[\/\:]/o) {
305			my $repo;
306			($repo, $pkgname) = $state->repo->path_parse($pkgname);
307			$set->add_repositories($repo);
308		};
309		my $l = $state->updater->stem2location($set, $pkgname, $state,
310		    $set->{quirks});
311		if (defined $l) {
312			$self->add_location($set, $hint, $l);
313		} else {
314			return undef;
315		}
316	} else {
317		if (!defined $cache->{$pkgname}) {
318			$self->add_handle($set, $hint, OpenBSD::Handle->create_new($pkgname));
319			$cache->{$pkgname} = 1;
320		}
321	}
322	OpenBSD::Add::tag_user_packages($set);
323	return 1;
324}
325
326sub process_set
327{
328	my ($self, $set, $state) = @_;
329	my @problems = ();
330	for my $h ($set->older, $set->hints) {
331		next if $h->{update_found};
332		if (!defined $h->update($self, $set, $state)) {
333			push(@problems, $h->pkgname);
334		}
335	}
336	if (@problems > 0) {
337		$state->tracker->cant($set) if !$set->{quirks};
338		if ($set->{updates} != 0) {
339			$state->say("Can't update #1: no update found for #2",
340			    $set->print, join(',', @problems));
341		}
342		return 0;
343	} elsif ($set->{updates} == 0) {
344		$state->tracker->uptodate($set);
345		return 0;
346	}
347	$state->tracker->add_set($set);
348	return 1;
349}
350
351sub stem2location
352{
353	my ($self, $locator, $name, $state, $is_quirks) = @_;
354	my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name));
355	if (@$l > 1 && !$state->defines('allversions')) {
356		$l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l);
357	}
358	return $state->choose_location($name, $l, $is_quirks);
359}
360
3611;
362