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