xref: /openbsd/usr.sbin/pkg_add/OpenBSD/Update.pm (revision cca36db2)
1# ex:ts=8 sw=4:
2# $OpenBSD: Update.pm,v 1.152 2012/04/28 11:55:16 espie Exp $
3#
4# Copyright (c) 2004-2010 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	if ($state->{wantntogo}) {
76		$msg .= " (".$state->ntogo.")";
77	}
78	$state->progress->message($msg);
79	$state->say($msg) if $state->verbose >= 2;
80}
81
82sub process_handle
83{
84	my ($self, $set, $h, $state) = @_;
85	my $pkgname = $h->pkgname;
86
87	if ($pkgname =~ m/^\.libs\d*\-/o) {
88		return 0;
89	}
90
91	my $base = 0;
92	eval {
93		$base = $state->quirks->is_base_system($h, $state);
94	};
95	if ($base) {
96		$h->{update_found} = OpenBSD::Handle->system;
97		$set->{updates}++;
98		return 1;
99	}
100
101	my $plist = OpenBSD::PackingList->from_installation($pkgname,
102	    \&OpenBSD::PackingList::UpdateInfoOnly);
103	if (!defined $plist) {
104		$state->fatal("can't locate #1", $pkgname);
105	}
106
107	if ($plist->has('explicit-update') && $state->{allupdates}) {
108		$h->{update_found} = $h;
109		$set->move_kept($h);
110		return 0;
111	}
112
113#	if (defined $plist->{url}) {
114#		my $repo;
115#		($repo, undef) = $state->repo->path_parse($plist->{url}->name);
116#		$set->add_repositories($repo);
117#	}
118	my @search = ();
119
120	my $sname = $pkgname;
121	while ($sname =~ s/^partial\-//o) {
122	}
123	push(@search, OpenBSD::Search::Stem->split($sname));
124
125	eval {
126		$state->quirks->tweak_search(\@search, $h, $state);
127	};
128	my $oldfound = 0;
129
130	# XXX this is nasty: maybe we added an old set to update
131	# because of conflicts, in which case the pkgpath +
132	# conflict should be enough  to "match".
133	for my $n ($set->newer) {
134		if (($state->{hard_replace} ||
135		    $n->location->update_info->match_pkgpath($plist)) &&
136			defined $n->plist &&
137			$n->plist->conflict_list->conflicts_with($sname)) {
138				$self->add_handle($set, $h, $n);
139				return 1;
140		}
141	}
142	if (!$state->defines('downgrade')) {
143		push(@search, OpenBSD::Search::FilterLocation->more_recent_than($sname, \$oldfound));
144	}
145	push(@search, OpenBSD::Search::FilterLocation->new(
146	    sub {
147		my $l = shift;
148		if (@$l == 0) {
149			return $l;
150		}
151		my @l2 = ();
152		for my $loc (@$l) {
153		    if (!$loc) {
154			    next;
155		    }
156		    my $p2 = $loc->update_info;
157		    if (!$p2) {
158			next;
159		    }
160		    if ($p2->has('arch')) {
161			unless ($p2->{arch}->check($state->{arch})) {
162			    $loc->forget;
163			    next;
164			}
165		    }
166		    if (!$plist->match_pkgpath($p2)) {
167		    	$loc->forget;
168			next
169		    }
170		    if ($p2->has('explicit-update') && $state->{allupdates}) {
171			$oldfound = 1;
172			$loc->forget;
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	if (@$l == 0) {
192		if ($oldfound) {
193			$h->{update_found} = $h;
194			$set->move_kept($h);
195
196			$self->progress_message($state,
197			    "No need to update #1", $pkgname);
198
199			return 0;
200		}
201		return undef;
202	}
203	$state->say("Update candidates: #1 -> #2 (#3)", $pkgname,
204	    join(' ', map {$_->name} @$l), $state->ntogo) if $state->verbose;
205
206	my $r = $state->choose_location($pkgname, $l);
207	if (defined $r) {
208		$self->add_location($set, $h, $r);
209		return 1;
210	} else {
211		$state->{issues} = 1;
212		return undef;
213	}
214}
215
216sub find_nearest
217{
218	my ($base, $locs) = @_;
219
220	my $pkgname = OpenBSD::PackageName->from_string($base);
221	return undef if !defined $pkgname->{version};
222	my @sorted = sort {$a->pkgname->{version}->compare($b->pkgname->{version}) } @$locs;
223	if ($sorted[0]->pkgname->{version}->compare($pkgname->{version}) > 0) {
224		return $sorted[0];
225	}
226	if ($sorted[-1]->pkgname->{version}->compare($pkgname->{version}) < 0) {
227		return $sorted[-1];
228	}
229	return undef;
230}
231
232sub process_hint
233{
234	my ($self, $set, $hint, $state) = @_;
235
236	my $l;
237	my $hint_name = $hint->pkgname;
238	my $k = OpenBSD::Search::FilterLocation->keep_most_recent;
239	# first try to find us exactly
240
241	$self->progress_message($state, "Looking for #1", $hint_name);
242	$l = $set->match_locations(OpenBSD::Search::Exact->new($hint_name), $k);
243	if (@$l == 0) {
244		my $t = $hint_name;
245		$t =~ s/\-\d([^-]*)\-?/--/;
246		$l = $set->match_locations(OpenBSD::Search::Stem->new($t), $k);
247	}
248	if (@$l > 1) {
249		my $r = find_nearest($hint_name, $l);
250		if (defined $r) {
251			$self->add_location($set, $hint, $r);
252			return 1;
253		}
254	}
255	my $r = $state->choose_location($hint_name, $l);
256	if (defined $r) {
257		$self->add_location($set, $hint, $r);
258		OpenBSD::Add::tag_user_packages($set);
259		return 1;
260	} else {
261		return 0;
262	}
263}
264
265my $cache = {};
266
267sub process_hint2
268{
269	my ($self, $set, $hint, $state) = @_;
270	my $pkgname = $hint->pkgname;
271	if (OpenBSD::PackageName::is_stem($pkgname)) {
272		if ($pkgname =~ m/[\/\:]/o) {
273			my $repo;
274			($repo, $pkgname) = $state->repo->path_parse($pkgname);
275			$set->add_repositories($repo);
276		};
277		my $l = $state->updater->stem2location($set, $pkgname, $state,
278		    $set->{quirks});
279		if (defined $l) {
280			$self->add_location($set, $hint, $l);
281		} else {
282			return undef;
283		}
284	} else {
285		if (!defined $cache->{$pkgname}) {
286			$self->add_handle($set, $hint, OpenBSD::Handle->create_new($pkgname));
287			$cache->{$pkgname} = 1;
288		}
289	}
290	OpenBSD::Add::tag_user_packages($set);
291	return 1;
292}
293
294sub process_set
295{
296	my ($self, $set, $state) = @_;
297	my @problems = ();
298	for my $h ($set->older, $set->hints) {
299		next if $h->{update_found};
300		if (!defined $h->update($self, $set, $state)) {
301			push(@problems, $h->pkgname);
302		}
303	}
304	if (@problems > 0) {
305		$state->tracker->cant($set) if !$set->{quirks};
306		if ($set->{updates} != 0) {
307			$state->say("Can't update #1: no update found for #2",
308			    $set->print, join(',', @problems));
309		}
310		return 0;
311	} elsif ($set->{updates} == 0) {
312		$state->tracker->uptodate($set);
313		return 0;
314	}
315	$state->tracker->add_set($set);
316	return 1;
317}
318
319sub stem2location
320{
321	my ($self, $locator, $name, $state, $is_quirks) = @_;
322	my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name));
323	if (@$l > 1 && !$state->defines('allversions')) {
324		$l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l);
325	}
326	return $state->choose_location($name, $l, $is_quirks);
327}
328
3291;
330