1cb05967dSespie# ex:ts=8 sw=4: 2*039cbdaaSespie# $OpenBSD: UpdateSet.pm,v 1.89 2023/06/13 09:07:17 espie Exp $ 3cb05967dSespie# 463e11790Sespie# Copyright (c) 2007-2010 Marc Espie <espie@openbsd.org> 5cb05967dSespie# 6cb05967dSespie# Permission to use, copy, modify, and distribute this software for any 7cb05967dSespie# purpose with or without fee is hereby granted, provided that the above 8cb05967dSespie# copyright notice and this permission notice appear in all copies. 9cb05967dSespie# 10cb05967dSespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11cb05967dSespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12cb05967dSespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13cb05967dSespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14cb05967dSespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15cb05967dSespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16cb05967dSespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17cb05967dSespie 1859730165Sespie 1959730165Sespie# an UpdateSet is a list of packages to remove/install. 202172b28bSespie# it contains several things: 2159730165Sespie# -> a list of older packages to remove (installed locations) 2259730165Sespie# -> a list of newer packages to add (might be very simple locations) 2359730165Sespie# -> a list of "hints", as package names to install 242172b28bSespie# -> a list of packages that are kept throughout an update 2559730165Sespie# every add/remove operations manipulate UpdateSet. 2659730165Sespie# 2759730165Sespie# Since older packages are always installed, they're organized as a hash. 2859730165Sespie# 2959730165Sespie# XXX: an UpdateSet succeeds or fails "together". 3059730165Sespie# if several packages should be removed/added, then not being able 3159730165Sespie# to do stuff on ONE of them is enough to invalidate the whole set. 3259730165Sespie# 3359730165Sespie# Normal UpdateSets contain one newer package at most. 3459730165Sespie# Bigger UpdateSets can be created through the merge operation, which 3559730165Sespie# will be used only when necessary. 362172b28bSespie# 372172b28bSespie# kept packages are needed after merges, where some dependencies may 382172b28bSespie# not need updating, and to distinguish from old packages that will be 392172b28bSespie# removed. 402172b28bSespie# 412172b28bSespie# for instance, package installation will check UpdateSets for internal 422172b28bSespie# dependencies and for conflicts. For that to work, we need kept stuff 432172b28bSespie# 44*039cbdaaSespieuse v5.36; 45f76cebffSespie 464c82e4b5Sespie# hints should behave like locations 474c82e4b5Sespiepackage OpenBSD::hint; 48*039cbdaaSespiesub new($class, $name) 494c82e4b5Sespie{ 504c82e4b5Sespie bless {name => $name}, $class; 514c82e4b5Sespie} 524c82e4b5Sespie 53*039cbdaaSespiesub pkgname($self) 544c82e4b5Sespie{ 55*039cbdaaSespie return $self->{name}; 564c82e4b5Sespie} 574c82e4b5Sespie 58e05ce761Sespiepackage OpenBSD::hint2; 59e05ce761Sespieour @ISA = qw(OpenBSD::hint); 60e05ce761Sespie 61a0ae1cf1Sespie# Code organisation: this is the stuff that's common for actual UpdateSets 62a0ae1cf1Sespie# (used by pkg_add) and DeleteSets, a simpler version used by pkg_delete. 63a0ae1cf1Sespie# Turns out some of that stuff is identical. 64a0ae1cf1Sespie# The really juicy stuff resides in pkg_add/pkg_delete proper. 65d72bb06fSespiepackage OpenBSD::DeleteSet; 661344a963Sespieuse OpenBSD::Error; 671344a963Sespie 68*039cbdaaSespiesub new($class, $state) 69d72bb06fSespie{ 70d72bb06fSespie return bless {older => {}}, $class; 71d72bb06fSespie} 72d72bb06fSespie 73*039cbdaaSespiesub add_older($self, @p) 74d72bb06fSespie{ 75*039cbdaaSespie for my $h (@p) { 769a9cac3bSespie $self->{older}{$h->pkgname} = $h; 779a9cac3bSespie $h->{is_old} = 1; 78d72bb06fSespie } 79d72bb06fSespie return $self; 80d72bb06fSespie} 81d72bb06fSespie 82*039cbdaaSespiesub older($self) 83d72bb06fSespie{ 84d72bb06fSespie return values %{$self->{older}}; 85d72bb06fSespie} 86d72bb06fSespie 87*039cbdaaSespiesub older_names($self) 88d72bb06fSespie{ 89d72bb06fSespie return keys %{$self->{older}}; 90d72bb06fSespie} 91d72bb06fSespie 92*039cbdaaSespiesub all_handles # forwarder 93d72bb06fSespie{ 94d72bb06fSespie &older; 95d72bb06fSespie} 96d72bb06fSespie 97*039cbdaaSespiesub changed_handles # forwarder 98f3ff8f9eSespie{ 99f3ff8f9eSespie &older; 100f3ff8f9eSespie} 101f3ff8f9eSespie 102*039cbdaaSespiesub mark_as_finished($self) 103d72bb06fSespie{ 104d72bb06fSespie $self->{finished} = 1; 105d72bb06fSespie} 106d72bb06fSespie 107*039cbdaaSespiesub cleanup($self, $error = undef, $errorinfo = undef) 108d72bb06fSespie{ 109d72bb06fSespie for my $h ($self->all_handles) { 110d72bb06fSespie $h->cleanup($error, $errorinfo); 111d72bb06fSespie } 112a92fd548Sespie if (defined $error) { 113d72bb06fSespie $self->{error} //= $error; 114d72bb06fSespie $self->{errorinfo} //= $errorinfo; 115a92fd548Sespie } 1163dafbabdSespie delete $self->{solver}; 117d957d6edSespie delete $self->{known_mandirs}; 118d957d6edSespie delete $self->{known_displays}; 119d66db768Sespie delete $self->{dont_delete}; 1202fcee420Sespie delete $self->{known_extra}; 1212fcee420Sespie delete $self->{known_sample}; 122d72bb06fSespie $self->mark_as_finished; 123d72bb06fSespie} 124d72bb06fSespie 125*039cbdaaSespiesub has_error # forwarder 126d72bb06fSespie{ 127d72bb06fSespie &OpenBSD::Handle::has_error; 128d72bb06fSespie} 129d72bb06fSespie 130a0ae1cf1Sespie# display code that will put together packages with the same version 131*039cbdaaSespiesub smart_join($self, @p) 13275ad74c9Sespie{ 133*039cbdaaSespie if (@p <= 1) { 134*039cbdaaSespie return join('+', @p); 13575ad74c9Sespie } 13675ad74c9Sespie my ($k, @stems); 137*039cbdaaSespie for my $l (@p) { 13875ad74c9Sespie my ($stem, @rest) = OpenBSD::PackageName::splitname($l); 13975ad74c9Sespie my $k2 = join('-', @rest); 14075ad74c9Sespie $k //= $k2; 14175ad74c9Sespie if ($k2 ne $k) { 142*039cbdaaSespie return join('+', sort @p); 14375ad74c9Sespie } 14475ad74c9Sespie push(@stems, $stem); 14575ad74c9Sespie } 14675ad74c9Sespie return join('+', sort @stems).'-'.$k; 14775ad74c9Sespie} 14875ad74c9Sespie 149*039cbdaaSespiesub print($self) 150d72bb06fSespie{ 15175ad74c9Sespie return $self->smart_join($self->older_names); 152d72bb06fSespie} 153d72bb06fSespie 154*039cbdaaSespiesub todo_names # forwarder 155d72bb06fSespie{ 156d72bb06fSespie &older_names; 157d72bb06fSespie} 158d72bb06fSespie 159*039cbdaaSespiesub short_print($self) 160d72bb06fSespie{ 16175ad74c9Sespie my $result = $self->smart_join($self->todo_names); 162d72bb06fSespie if (length $result > 30) { 163d72bb06fSespie return substr($result, 0, 27)."..."; 164d72bb06fSespie } else { 165d72bb06fSespie return $result; 166d72bb06fSespie } 167d72bb06fSespie} 168d72bb06fSespie 169*039cbdaaSespiesub real_set($set) 170d72bb06fSespie{ 171d72bb06fSespie while (defined $set->{merged}) { 172d72bb06fSespie $set = $set->{merged}; 173d72bb06fSespie } 174d72bb06fSespie return $set; 175d72bb06fSespie} 176d72bb06fSespie 177*039cbdaaSespiesub merge_set($self, $set) 178d72bb06fSespie{ 179d72bb06fSespie $self->add_older($set->older); 180d72bb06fSespie $set->mark_as_finished; 181d72bb06fSespie # XXX and mark it as merged, for eventual updates 182d72bb06fSespie $set->{merged} = $self; 183d72bb06fSespie} 184d72bb06fSespie 185d72bb06fSespie# Merge several deletesets together 186*039cbdaaSespiesub merge($self, $tracker, @sets) 187d72bb06fSespie{ 188d72bb06fSespie # Apparently simple, just add the missing parts 189d72bb06fSespie for my $set (@sets) { 190d72bb06fSespie next if $set eq $self; 191d72bb06fSespie $self->merge_set($set); 192d72bb06fSespie $tracker->handle_set($set); 193d72bb06fSespie } 194d72bb06fSespie # then regen tracker info for $self 195d72bb06fSespie $tracker->todo($self); 196d72bb06fSespie return $self; 197d72bb06fSespie} 198d72bb06fSespie 199*039cbdaaSespiesub match_locations($, @) 200b8b880d2Sespie{ 201b8b880d2Sespie return []; 202b8b880d2Sespie} 203b8b880d2Sespie 2041344a963SespieOpenBSD::Auto::cache(solver, 205*039cbdaaSespie sub($self) { 2061344a963Sespie require OpenBSD::Dependencies; 207*039cbdaaSespie return OpenBSD::Dependencies::Solver->new($self); 2081344a963Sespie }); 2091344a963Sespie 2101344a963SespieOpenBSD::Auto::cache(conflict_cache, 211*039cbdaaSespie sub($) { 2121344a963Sespie require OpenBSD::Dependencies; 2131344a963Sespie return OpenBSD::ConflictCache->new; 2141344a963Sespie }); 2151344a963Sespie 21616008a12Sespiepackage OpenBSD::UpdateSet; 217d72bb06fSespieour @ISA = qw(OpenBSD::DeleteSet); 21898cf79f0Sespie 219*039cbdaaSespiesub new($class, $state) 22016008a12Sespie{ 221d72bb06fSespie my $o = $class->SUPER::new($state); 222d72bb06fSespie $o->{newer} = {}; 223d72bb06fSespie $o->{kept} = {}; 224d72bb06fSespie $o->{repo} = $state->repo; 225d72bb06fSespie $o->{hints} = []; 226d72bb06fSespie $o->{updates} = 0; 227d72bb06fSespie return $o; 22816008a12Sespie} 22916008a12Sespie 230*039cbdaaSespie# TODO this stuff is mostly unused right now (or buggy) 231*039cbdaaSespiesub path($set) 2327087d2adSespie{ 2337087d2adSespie return $set->{path}; 2347087d2adSespie} 2357087d2adSespie 236*039cbdaaSespiesub add_repositories($set, @repos) 2377087d2adSespie{ 2387087d2adSespie if (!defined $set->{path}) { 23968a8a69cSespie $set->{path} = $set->{repo}->path; 2407087d2adSespie } 2417087d2adSespie $set->{path}->add(@repos); 2427087d2adSespie} 2437087d2adSespie 244*039cbdaaSespiesub merge_paths($set, $other) 24546077820Sespie{ 24646077820Sespie if (defined $other->path) { 24746077820Sespie if (!defined $set->path) { 24846077820Sespie $set->{path} = $other->path; 24946077820Sespie } elsif ($set->{path} ne $other->path) { 25046077820Sespie $set->add_path(@{$other->{path}}); 25146077820Sespie } 25246077820Sespie } 25346077820Sespie} 25446077820Sespie 255*039cbdaaSespiesub match_locations($set, @spec) 2567087d2adSespie{ 2577087d2adSespie my $r = []; 2587087d2adSespie if (defined $set->{path}) { 2597087d2adSespie $r = $set->{path}->match_locations(@spec); 2607087d2adSespie } 2617087d2adSespie if (@$r == 0) { 26268a8a69cSespie $r = $set->{repo}->match_locations(@spec); 2637087d2adSespie } 2647087d2adSespie return $r; 2657087d2adSespie} 2667087d2adSespie 267*039cbdaaSespiesub add_newer($self, @p) 26816008a12Sespie{ 269*039cbdaaSespie for my $h (@p) { 2709a9cac3bSespie $self->{newer}{$h->pkgname} = $h; 2710849d6feSespie $self->{updates}++; 272e05ce761Sespie } 273e05ce761Sespie return $self; 274e05ce761Sespie} 275e05ce761Sespie 276*039cbdaaSespiesub add_kept($self, @p) 2771ac97267Sespie{ 278*039cbdaaSespie for my $h (@p) { 2791ac97267Sespie $self->{kept}->{$h->pkgname} = $h; 2801ac97267Sespie } 2811ac97267Sespie return $self; 2821ac97267Sespie} 2831ac97267Sespie 284*039cbdaaSespiesub move_kept($self, @p) 2852172b28bSespie{ 286*039cbdaaSespie for my $h (@p) { 2872c3f8ba0Sespie delete $self->{older}{$h->pkgname}; 2882c3f8ba0Sespie delete $self->{newer}{$h->pkgname}; 2892c3f8ba0Sespie $self->{kept}{$h->pkgname} = $h; 2907571f4ebSespie if (!defined $h->{location}) { 2917571f4ebSespie $h->{location} = 2927571f4ebSespie $self->{repo}->installed->find($h->pkgname); 2937571f4ebSespie } 294bbe3831fSespie $h->complete_dependency_info; 295bbe3831fSespie $h->{update_found} = $h; 2962172b28bSespie } 2972172b28bSespie return $self; 2982172b28bSespie} 2992172b28bSespie 300*039cbdaaSespiesub add_hints($self, @p) 301298306aeSespie{ 302*039cbdaaSespie for my $h (@p) { 303298306aeSespie push(@{$self->{hints}}, OpenBSD::hint->new($h)); 304298306aeSespie } 305298306aeSespie return $self; 306298306aeSespie} 307298306aeSespie 308*039cbdaaSespiesub add_hints2($self, @p) 309298306aeSespie{ 310*039cbdaaSespie for my $h (@p) { 311298306aeSespie push(@{$self->{hints}}, OpenBSD::hint2->new($h)); 312298306aeSespie } 313298306aeSespie return $self; 314298306aeSespie} 315298306aeSespie 316*039cbdaaSespiesub newer($self) 31716008a12Sespie{ 318298306aeSespie return values %{$self->{newer}}; 31916008a12Sespie} 32016008a12Sespie 321*039cbdaaSespiesub kept($self) 3222172b28bSespie{ 3232172b28bSespie return values %{$self->{kept}}; 3242172b28bSespie} 3252172b28bSespie 326*039cbdaaSespiesub hints($self) 3277d63c3f8Sespie{ 3287d63c3f8Sespie return @{$self->{hints}}; 3297d63c3f8Sespie} 3307d63c3f8Sespie 331*039cbdaaSespiesub newer_names($self) 332dbfb28daSespie{ 333298306aeSespie return keys %{$self->{newer}}; 334298306aeSespie} 335298306aeSespie 336*039cbdaaSespiesub kept_names($self) 3372172b28bSespie{ 3382172b28bSespie return keys %{$self->{kept}}; 3392172b28bSespie} 3402172b28bSespie 341*039cbdaaSespiesub all_handles($self) 342d72bb06fSespie{ 343d72bb06fSespie return ($self->older, $self->newer, $self->kept); 344d72bb06fSespie} 345d72bb06fSespie 346*039cbdaaSespiesub changed_handles($self) 347f3ff8f9eSespie{ 348f3ff8f9eSespie return ($self->older, $self->newer); 349f3ff8f9eSespie} 350f3ff8f9eSespie 351*039cbdaaSespiesub hint_names($self) 352298306aeSespie{ 353298306aeSespie return map {$_->pkgname} $self->hints; 35416008a12Sespie} 35516008a12Sespie 356*039cbdaaSespiesub older_to_do($self) 35716008a12Sespie{ 35816008a12Sespie # XXX in `combined' updates, some dependencies may remove extra 35916008a12Sespie # packages, so we do a double-take on the list of packages we 36016008a12Sespie # are actually replacing... for now, until we merge update sets. 36116008a12Sespie require OpenBSD::PackageInfo; 36216008a12Sespie my @l = (); 36316008a12Sespie for my $h ($self->older) { 3642172b28bSespie if (OpenBSD::PackageInfo::is_installed($h->pkgname)) { 36516008a12Sespie push(@l, $h); 36616008a12Sespie } 36716008a12Sespie } 36816008a12Sespie return @l; 36916008a12Sespie} 37016008a12Sespie 371*039cbdaaSespiesub print($self) 37216008a12Sespie{ 373475c1fd5Sespie my $result = ""; 3742172b28bSespie if ($self->kept > 0) { 37575ad74c9Sespie $result = "[".$self->smart_join($self->kept_names)."]"; 37675ad74c9Sespie } 37775ad74c9Sespie my ($old, $new); 37875ad74c9Sespie if ($self->older > 0) { 37975ad74c9Sespie $old = $self->SUPER::print; 38075ad74c9Sespie } 38175ad74c9Sespie if ($self->newer > 0) { 38275ad74c9Sespie $new = $self->smart_join($self->newer_names); 3832172b28bSespie } 3840cc88a49Sespie # XXX common case 38575ad74c9Sespie if (defined $old && defined $new) { 3860fc00915Sespie my ($stema, @resta) = OpenBSD::PackageName::splitname($old); 3870fc00915Sespie my $resta = join('-', @resta); 3880fc00915Sespie my ($stemb, @restb) = OpenBSD::PackageName::splitname($new); 3890fc00915Sespie my $restb = join('-', @restb); 3900fc00915Sespie if ($stema eq $stemb && $resta !~ /\+/ && $restb !~ /\+/) { 3910fc00915Sespie return $result .$old."->".$restb; 3920cc88a49Sespie } 3930cc88a49Sespie } 3940cc88a49Sespie 39575ad74c9Sespie if (defined $old) { 39675ad74c9Sespie $result .= $old."->"; 39716008a12Sespie } 39875ad74c9Sespie if (defined $new) { 39975ad74c9Sespie $result .= $new; 400475c1fd5Sespie } elsif ($self->hints > 0) { 40175ad74c9Sespie $result .= $self->smart_join($self->hint_names); 402475c1fd5Sespie } 403475c1fd5Sespie return $result; 40416008a12Sespie} 40516008a12Sespie 406*039cbdaaSespiesub todo_names($self) 407b07acb4aSespie{ 4082a5be836Sespie if ($self->newer > 0) { 4092a5be836Sespie return $self->newer_names; 4102a5be836Sespie } else { 4112a5be836Sespie return $self->kept_names; 4122a5be836Sespie } 413b1238d28Sespie} 414b1238d28Sespie 415*039cbdaaSespiesub validate_plists($self, $state) 41616008a12Sespie{ 41716008a12Sespie $state->{problems} = 0; 4181a1aaf83Sespie delete $state->{overflow}; 41916008a12Sespie 4208221480eSespie $state->{current_set} = $self; 4218221480eSespie 42216008a12Sespie for my $o ($self->older_to_do) { 42316008a12Sespie require OpenBSD::Delete; 42416008a12Sespie OpenBSD::Delete::validate_plist($o->{plist}, $state); 42516008a12Sespie } 42616008a12Sespie $state->{colliding} = []; 42716008a12Sespie for my $n ($self->newer) { 42816008a12Sespie require OpenBSD::Add; 429062c25acSespie OpenBSD::Add::validate_plist($n->{plist}, $state, $self); 43016008a12Sespie } 43116008a12Sespie if (@{$state->{colliding}} > 0) { 43216008a12Sespie require OpenBSD::CollisionReport; 43316008a12Sespie 434b5c9b1b8Sespie OpenBSD::CollisionReport::collision_report($state->{colliding}, $state, $self); 43516008a12Sespie } 43616008a12Sespie if (defined $state->{overflow}) { 4374c82e4b5Sespie $state->vstat->tally; 4383c675cdaSespie $state->vstat->drop_changes; 4393c675cdaSespie # nothing to try if we don't have existing stuff to remove 4403c675cdaSespie return 0 if $self->older == 0; 4413c675cdaSespie # we already tried the other way around... 4423c675cdaSespie return 0 if $state->{delete_first}; 443a409537dSespie if ($state->defines('deletefirst') || 444fb75580aSespie $state->confirm_defaults_to_no( 445fb75580aSespie "Delete older packages first")) { 4463c675cdaSespie # okay we recurse doing things the other way around 4471a1aaf83Sespie $state->{delete_first} = 1; 4481a1aaf83Sespie return $self->validate_plists($state); 4491a1aaf83Sespie } 4501a1aaf83Sespie } 45116008a12Sespie if ($state->{problems}) { 452671c958dSespie $state->vstat->drop_changes; 453671c958dSespie return 0; 454671c958dSespie } else { 4554c82e4b5Sespie $state->vstat->synchronize; 456671c958dSespie return 1; 457671c958dSespie } 45816008a12Sespie} 45916008a12Sespie 460*039cbdaaSespiesub cleanup_old_shared($set, $state) 4618221480eSespie{ 4628221480eSespie my $h = $set->{old_shared}; 4638221480eSespie 4648221480eSespie for my $d (sort {$b cmp $a} keys %$h) { 4658221480eSespie OpenBSD::SharedItems::wipe_directory($state, $h, $d) || 4668221480eSespie $state->fatal("Can't continue"); 4678221480eSespie delete $state->{recorder}{dirs}{$d}; 4688221480eSespie } 4698221480eSespie} 4708221480eSespie 471d72bb06fSespiemy @extra = qw(solver conflict_cache); 472*039cbdaaSespiesub mark_as_finished($self) 473d72bb06fSespie{ 474d72bb06fSespie for my $i (@extra, 'sha') { 475d72bb06fSespie delete $self->{$i}; 476d72bb06fSespie } 477d72bb06fSespie $self->SUPER::mark_as_finished; 478d72bb06fSespie} 479d72bb06fSespie 480*039cbdaaSespiesub merge_if_exists($self, $k, @extra) 4812bf3c32bSespie{ 4821ac97267Sespie my @list = (); 4831ac97267Sespie for my $s (@extra) { 4841ac97267Sespie if ($s ne $self && defined $s->{$k}) { 4851ac97267Sespie push(@list, $s->{$k}); 4862bf3c32bSespie } 4872bf3c32bSespie } 4881ac97267Sespie $self->$k->merge(@list); 4891ac97267Sespie} 4902bf3c32bSespie 491*039cbdaaSespiesub merge_set($self, $set) 492d72bb06fSespie{ 493d72bb06fSespie $self->SUPER::merge_set($set); 494d72bb06fSespie $self->add_newer($set->newer); 495d72bb06fSespie $self->add_kept($set->kept); 496d72bb06fSespie $self->merge_paths($set); 497d72bb06fSespie $self->{updates} += $set->{updates}; 498d72bb06fSespie $set->{updates} = 0; 499d72bb06fSespie} 500d72bb06fSespie 5016af57ca2Sespie# Merge several updatesets together 502*039cbdaaSespiesub merge($self, $tracker, @sets) 5036af57ca2Sespie{ 504d72bb06fSespie for my $i (@extra) { 505d72bb06fSespie $self->merge_if_exists($i, @sets); 5066af57ca2Sespie } 507d72bb06fSespie return $self->SUPER::merge($tracker, @sets); 508b07acb4aSespie} 509b07acb4aSespie 510cb05967dSespie1; 511