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