1# ex:ts=8 sw=4: 2# $OpenBSD: Update.pm,v 1.171 2023/10/07 09:10:03 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 v5.36; 18 19package OpenBSD::Handle; 20sub update($self, $updater, $set, $state) 21{ 22 23 return $updater->process_handle($set, $self, $state); 24} 25 26# TODO hint and hint2 are horrible names 27package OpenBSD::hint; 28sub update($self, $updater, $set, $state) 29{ 30 31 return $updater->process_hint($set, $self, $state); 32} 33 34package OpenBSD::hint2; 35sub update($self, $updater, $set, $state) 36{ 37 return $updater->process_hint2($set, $self, $state); 38} 39 40package OpenBSD::Update; 41use OpenBSD::PackageInfo; 42use OpenBSD::PackageName; 43use OpenBSD::Error; 44use OpenBSD::UpdateSet; 45 46sub new($class) 47{ 48 return bless {}, $class; 49} 50 51sub add_handle($self, $set, $old, $n) 52{ 53 $old->{update_found} = $n; 54 $set->add_newer($n); 55} 56 57sub add_location($self, $set, $handle, $location) 58{ 59 $self->add_handle($set, $handle, 60 OpenBSD::Handle->from_location($location)); 61} 62 63sub look_for_debug($self, $set, $oldname, $newname, $state) 64{ 65 # hurdles to pass before adding debug packages 66 return unless $state->{debug_packages}; 67 68 return if $state->tracker->is_to_update("debug-".$oldname); 69 my $dbg = "debug-".$newname; 70 my $l = $set->match_locations(OpenBSD::Search::Exact->new($dbg)); 71 # TODO if @$l == 0, I should look for other packages with similar names 72 # just so I can warn for out-of-date/shearing in the mirrors. 73 return if @$l != 1; 74 $set->add_newer(OpenBSD::Handle->from_location($l->[0])); 75} 76 77sub found_update($self, $set, $old, $location, $state) 78{ 79 $self->add_location($set, $old, $location); 80 $self->look_for_debug($set, $old->pkgname, $location->name, $state); 81} 82 83sub progress_message($self, $state, @r) 84{ 85 my $msg = $state->f(@r); 86 $msg .= $state->ntogo_string; 87 $state->progress->message($msg); 88 $state->say($msg) if $state->verbose >= 2; 89} 90 91sub process_handle($self, $set, $h, $state) 92{ 93 my $pkgname = $h->pkgname; 94 95 if ($pkgname =~ m/^\.libs\d*\-/o) { 96 return 0; 97 } 98 99 if (!$set->{quirks}) { 100 my $base = 0; 101 $state->run_quirks( 102 sub($quirks) { 103 $base = $quirks->is_base_system($h, $state); 104 }); 105 if ($base) { 106 $h->{update_found} = OpenBSD::Handle->system; 107 $set->{updates}++; 108 return 1; 109 } 110 } 111 112 my $plist = OpenBSD::PackingList->from_installation($pkgname, 113 \&OpenBSD::PackingList::UpdateInfoOnly); 114 if (!defined $plist) { 115 $state->fatal("can't locate #1", $pkgname); 116 } 117 118 if ($plist->has('firmware') && !$state->defines('FW_UPDATE')) { 119 $set->move_kept($h); 120 $h->{is_firmware} = 1; 121 return 0; 122 } 123 124# if (defined $plist->{url}) { 125# my $repo; 126# ($repo, undef) = $state->repo->path_parse($plist->{url}->name); 127# $set->add_repositories($repo); 128# } 129 my @search = (); 130 131 my $sname = $pkgname; 132 while ($sname =~ s/^partial\-//o) { 133 } 134 push(@search, OpenBSD::Search::Stem->split($sname)); 135 136 if (!$set->{quirks}) { 137 $state->run_quirks( 138 sub($quirks) { 139 $quirks->tweak_search(\@search, $h, $state); 140 }); 141 } 142 my $oldfound = 0; 143 my @skipped_locs = (); 144 145 # XXX this is nasty: maybe we added an old set to update 146 # because of conflicts, in which case the pkgpath + 147 # conflict should be enough to "match". 148 for my $n ($set->newer) { 149 if (($state->{hard_replace} || 150 $n->location->update_info->match_pkgpath($plist)) && 151 $n->conflict_list->conflicts_with($sname)) { 152 $self->add_handle($set, $h, $n); 153 return 1; 154 } 155 } 156 # XXX all that code conveniently forgets about old versions, while 157 # marking them as "normal". 158 # there should be some error path when we consistently fail to find 159 # an equal-or-newer version in our repository, so that pkg_add has 160 # consistent exit codes. 161 if (!$state->defines('downgrade')) { 162 push(@search, OpenBSD::Search::FilterLocation->more_recent_than($sname, \$oldfound)); 163 } 164 push(@search, OpenBSD::Search::FilterLocation->new( 165 sub($l) { 166 if (@$l == 0) { 167 return $l; 168 } 169 my @l2 = (); 170 for my $loc (@$l) { 171 if (!$loc) { 172 next; 173 } 174 my $p2 = $loc->update_info; 175 if (!$p2) { 176 next; 177 } 178 if ($p2->has('arch')) { 179 unless ($p2->{arch}->check($state->{arch})) { 180 $loc->forget; 181 next; 182 } 183 } 184 if (!$plist->match_pkgpath($p2)) { 185 push(@skipped_locs, $loc); 186 next 187 } 188 my $r = $plist->signature->compare($p2->signature, $state); 189 if (defined $r && $r > 0 && !$state->defines('downgrade')) { 190 $oldfound = 1; 191 $loc->forget; 192 next; 193 } 194 push(@l2, $loc); 195 } 196 return \@l2; 197 })); 198 199 if (!$state->defines('allversions')) { 200 push(@search, OpenBSD::Search::FilterLocation->keep_most_recent); 201 } 202 203 my $l = $set->match_locations(@search); 204 205 for my $loc (@skipped_locs) { 206 if (@$l == 0 && $state->verbose) { 207 $self->say_skipped_packages($state, $plist, 208 $loc->update_info); 209 } 210 $loc->forget; 211 } 212 213 if (@$l == 0) { 214 if ($oldfound) { 215 $set->move_kept($h); 216 $self->progress_message($state, 217 "No need to update #1", $pkgname); 218 $self->look_for_debug($set, $pkgname, $pkgname, $state); 219 return 0; 220 } 221 return undef; 222 } 223 $state->say("Update candidates: #1 -> #2#3", $pkgname, 224 join(' ', map {$_->name} @$l), $state->ntogo_string) 225 if $state->verbose; 226 227 my $r = $state->choose_location($pkgname, $l); 228 if (defined $r) { 229 $self->found_update($set, $h, $r, $state); 230 return 1; 231 } else { 232 $state->{issues} = 1; 233 return undef; 234 } 235} 236 237sub say_skipped_packages($self, $state, $o, $n) 238{ 239 my $o_name = $o->pkgname; 240 my @o_ps = map { @{$o->pkgpath->{$_}} } keys %{$o->pkgpath}; 241 my $o_pp = join(" ", map {$_->fullpkgpath} @o_ps); 242 243 my $n_name = $n->pkgname; 244 my @n_ps = map { @{$n->pkgpath->{$_}} } keys %{$n->pkgpath}; 245 my $n_pp= join(" ", map {$_->fullpkgpath} @n_ps); 246 247 my $t = "Skipping #1 (update candidate for #2)"; 248 $t .= "\n\t#2 pkgpaths: #4\n\t#1 pkgpaths: #3"; 249 250 $state->say($t, $n_name, $o_name, $n_pp, $o_pp); 251} 252 253sub find_nearest($base, $locs) 254{ 255 my $pkgname = OpenBSD::PackageName->from_string($base); 256 return undef if !defined $pkgname->{version}; 257 my @sorted = sort {$a->pkgname->{version}->compare($b->pkgname->{version}) } @$locs; 258 if ($sorted[0]->pkgname->{version}->compare($pkgname->{version}) > 0) { 259 return $sorted[0]; 260 } 261 if ($sorted[-1]->pkgname->{version}->compare($pkgname->{version}) < 0) { 262 return $sorted[-1]; 263 } 264 return undef; 265} 266 267sub process_hint($self, $set, $hint, $state) 268{ 269 my $l; 270 my $hint_name = $hint->pkgname; 271 my $k = OpenBSD::Search::FilterLocation->keep_most_recent; 272 # first try to find us exactly 273 274 $self->progress_message($state, "Looking for #1", $hint_name); 275 $l = $set->match_locations(OpenBSD::Search::Exact->new($hint_name), $k); 276 if (@$l == 0) { 277 my $t = $hint_name; 278 $t =~ s/\-\d([^-]*)\-?/--/; 279 my @search = (OpenBSD::Search::Stem->new($t)); 280 $state->run_quirks( 281 sub($quirks) { 282 $quirks->tweak_search(\@search, $hint, $state); 283 }); 284 $l = $set->match_locations(@search, $k); 285 } 286 if (@$l > 1) { 287 my $r = find_nearest($hint_name, $l); 288 if (defined $r) { 289 $self->found_update($set, $hint, $r, $state); 290 return 1; 291 } 292 } 293 my $r = $state->choose_location($hint_name, $l); 294 if (defined $r) { 295 $self->found_update($set, $hint, $r, $state); 296 OpenBSD::Add::tag_user_packages($set); 297 return 1; 298 } else { 299 return 0; 300 } 301} 302 303my $cache = {}; 304 305sub process_hint2($self, $set, $hint, $state) 306{ 307 my $pkgname = $hint->pkgname; 308 my $pkg2; 309 if ($pkgname =~ m/[\/\:]/o) { 310 my $repo; 311 ($repo, $pkg2) = $state->repo->path_parse($pkgname); 312 $set->add_repositories($repo); 313 } else { 314 $pkg2 = $pkgname; 315 } 316 if (OpenBSD::PackageName::is_stem($pkg2)) { 317 my $l = $state->updater->stem2location($set, $pkg2, $state, 318 $set->{quirks}); 319 if (defined $l) { 320 $self->add_location($set, $hint, $l); 321 $self->look_for_debug($set, $l->name, $l->name, $state); 322 } else { 323 return undef; 324 } 325 } else { 326 if (!defined $cache->{$pkgname}) { 327 $self->add_handle($set, $hint, OpenBSD::Handle->create_new($pkgname)); 328 $cache->{$pkgname} = 1; 329 $pkg2 =~ s/\.tgz$//; 330 $self->look_for_debug($set, $pkg2, $pkg2, $state); 331 } 332 } 333 OpenBSD::Add::tag_user_packages($set); 334 return 1; 335} 336 337sub process_set($self, $set, $state) 338{ 339 my @problems = (); 340 for my $h ($set->older, $set->hints) { 341 next if $h->{update_found}; 342 if (!defined $h->update($self, $set, $state)) { 343 push(@problems, $h->pkgname); 344 } 345 } 346 if (@problems > 0) { 347 $state->tracker->cant($set) if !$set->{quirks}; 348 if ($set->{updates} != 0) { 349 $state->say("Can't update #1: no update found for #2", 350 $set->print, join(',', @problems)); 351 } 352 return 0; 353 } elsif ($set->{updates} == 0) { 354 $state->tracker->uptodate($set); 355 return 0; 356 } 357 $state->tracker->add_set($set); 358 return 1; 359} 360 361sub stem2location($self, $locator, $name, $state, $is_quirks = 0) 362{ 363 my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name)); 364 if (@$l > 1 && !$state->defines('allversions')) { 365 $l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l); 366 } 367 return $state->choose_location($name, $l, $is_quirks); 368} 369 3701; 371