1a409537dSespie#!/usr/bin/perl 2a409537dSespie# ex:ts=8 sw=4: 3*ab16e3ccSespie# $OpenBSD: PkgDelete.pm,v 1.51 2023/10/09 07:12:22 espie Exp $ 4a409537dSespie# 5a409537dSespie# Copyright (c) 2003-2010 Marc Espie <espie@openbsd.org> 6a409537dSespie# 7a409537dSespie# Permission to use, copy, modify, and distribute this software for any 8a409537dSespie# purpose with or without fee is hereby granted, provided that the above 9a409537dSespie# copyright notice and this permission notice appear in all copies. 10a409537dSespie# 11a409537dSespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12a409537dSespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13a409537dSespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14a409537dSespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15a409537dSespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16a409537dSespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17a409537dSespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18a409537dSespie 19039cbdaaSespieuse v5.36; 20a409537dSespie 21a409537dSespieuse OpenBSD::AddDelete; 22a409537dSespie 23295c9761Sespie 24681090d4Sespiepackage OpenBSD::PkgDelete::Tracker; 25681090d4Sespie 26039cbdaaSespiesub new($class) 27681090d4Sespie{ 28681090d4Sespie bless {}, $class; 29681090d4Sespie} 30681090d4Sespie 31039cbdaaSespiesub sets_todo($self, $offset = 0) 32681090d4Sespie{ 33681090d4Sespie return sprintf("%u/%u", (scalar keys %{$self->{done}})-$offset, 34681090d4Sespie scalar keys %{$self->{total}}); 35681090d4Sespie} 36681090d4Sespie 37039cbdaaSespiesub handle_set($self, $set) 38681090d4Sespie{ 39681090d4Sespie $self->{total}{$set} = 1; 40681090d4Sespie if ($set->{finished}) { 41681090d4Sespie $self->{done}{$set} = 1; 42681090d4Sespie } 43681090d4Sespie} 44681090d4Sespie 45039cbdaaSespiesub todo($self, @list) 46681090d4Sespie{ 4746412527Sespie for my $set (@list) { 48681090d4Sespie for my $pkgname ($set->older_names) { 49681090d4Sespie $self->{todo}{$pkgname} = $set; 50681090d4Sespie } 51681090d4Sespie $self->handle_set($set); 52681090d4Sespie } 53681090d4Sespie} 54681090d4Sespie 55681090d4Sespie 56039cbdaaSespiesub done($self, $set) 57681090d4Sespie{ 58681090d4Sespie $set->{finished} = 1; 59681090d4Sespie for my $pkgname ($set->older_names) { 60681090d4Sespie delete $self->{todo}{$pkgname}; 61681090d4Sespie } 62681090d4Sespie $self->handle_set($set); 63681090d4Sespie} 64681090d4Sespie 65039cbdaaSespiesub cant # forwarder 6646412527Sespie{ 6746412527Sespie &done; 6846412527Sespie} 69039cbdaaSespie 70039cbdaaSespiesub find($self, $pkgname) 71681090d4Sespie{ 72681090d4Sespie return $self->{todo}{$pkgname}; 73681090d4Sespie} 74681090d4Sespie 75681090d4Sespie 76681090d4Sespie 77a409537dSespiepackage OpenBSD::PkgDelete::State; 78a409537dSespieour @ISA = qw(OpenBSD::AddDelete::State); 79a409537dSespie 80039cbdaaSespiesub new($class, @p) 81681090d4Sespie{ 82039cbdaaSespie my $self = $class->SUPER::new(@p); 83681090d4Sespie $self->{tracker} = OpenBSD::PkgDelete::Tracker->new; 84681090d4Sespie return $self; 85681090d4Sespie} 86681090d4Sespie 87039cbdaaSespiesub tracker($self) 88681090d4Sespie{ 89681090d4Sespie return $self->{tracker}; 90681090d4Sespie} 91681090d4Sespie 92039cbdaaSespiesub handle_options($state) 938a660df1Sespie{ 94bd849d19Sespie $state->SUPER::handle_options('X', 9552fcbadbSespie '[-acimnqsVvXx] [-B pkg-destdir] [-D name[=value]] [pkg-name ...]'); 968a660df1Sespie 97bd849d19Sespie $state->{exclude} = $state->opt('X'); 988a660df1Sespie} 998a660df1Sespie 100039cbdaaSespiesub stem2location($self, $locator, $name, $state) 101681090d4Sespie{ 102681090d4Sespie require OpenBSD::Search; 103681090d4Sespie my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name)); 104681090d4Sespie if (@$l > 1 && !$state->defines('allversions')) { 105681090d4Sespie $l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l); 106681090d4Sespie } 107681090d4Sespie return $state->choose_location($name, $l); 108a409537dSespie} 109a409537dSespie 110039cbdaaSespiesub deleteset($self) 111303a35c3Sespie{ 112303a35c3Sespie require OpenBSD::UpdateSet; 113303a35c3Sespie 114303a35c3Sespie return OpenBSD::DeleteSet->new($self); 115303a35c3Sespie} 116303a35c3Sespie 117039cbdaaSespiesub deleteset_from_location($self, $location) 118303a35c3Sespie{ 119303a35c3Sespie return $self->deleteset->add_older(OpenBSD::Handle->from_location($location)); 120303a35c3Sespie} 121303a35c3Sespie 122039cbdaaSespiesub solve_dependency($self, $solver, $dep, $package) 1235c974d94Sespie{ 1245c974d94Sespie # simpler dependency solving 1255c974d94Sespie return $solver->find_dep_in_installed($self, $dep); 1265c974d94Sespie} 1275c974d94Sespie 128d2f781d7Sespiepackage OpenBSD::DeleteSet; 129039cbdaaSespiesub setup_header($set, $state, $handle = undef) 130d2f781d7Sespie{ 131d2f781d7Sespie my $header = $state->deptree_header($set); 132d2f781d7Sespie if (defined $handle) { 133d2f781d7Sespie $header .= $handle->pkgname; 134d2f781d7Sespie } else { 135d2f781d7Sespie $header .= $set->print; 136d2f781d7Sespie } 137d2f781d7Sespie if (!$state->progress->set_header($header)) { 138d2f781d7Sespie return unless $state->verbose; 139d2f781d7Sespie $header = "Deleting $header"; 140d2f781d7Sespie if (defined $state->{lastheader} && 141d2f781d7Sespie $header eq $state->{lastheader}) { 142d2f781d7Sespie return; 143d2f781d7Sespie } 144d2f781d7Sespie $state->{lastheader} = $header; 145d2f781d7Sespie $state->print("#1", $header); 146d2f781d7Sespie $state->print("(pretending) ") if $state->{not}; 147d2f781d7Sespie $state->print("\n"); 148d2f781d7Sespie } 149d2f781d7Sespie} 150d2f781d7Sespie 151a409537dSespiepackage OpenBSD::PkgDelete; 152a409537dSespieour @ISA = qw(OpenBSD::AddDelete); 153a409537dSespie 154a409537dSespieuse OpenBSD::PackingList; 155a409537dSespieuse OpenBSD::RequiredBy; 156a409537dSespieuse OpenBSD::Delete; 157a409537dSespieuse OpenBSD::PackageInfo; 158a409537dSespieuse OpenBSD::UpdateSet; 159681090d4Sespieuse OpenBSD::Handle; 160a409537dSespie 161a409537dSespie 162039cbdaaSespiesub add_location($self, $state, $l) 163681090d4Sespie{ 164681090d4Sespie push(@{$state->{setlist}}, 165303a35c3Sespie $state->deleteset_from_location($l)); 166681090d4Sespie} 167681090d4Sespie 168039cbdaaSespiesub create_locations($state, @l) 169681090d4Sespie{ 170681090d4Sespie my $inst = $state->repo->installed; 171681090d4Sespie my $result = []; 172681090d4Sespie for my $name (@l) { 173f6c952a4Sespie my $l = $inst->find($name); 174681090d4Sespie if (!defined $l) { 175681090d4Sespie $state->errsay("Can't find #1 in installed packages", 176681090d4Sespie $name); 177681090d4Sespie $state->{bad}++; 178681090d4Sespie } else { 179303a35c3Sespie push(@$result, $state->deleteset_from_location($l)); 180681090d4Sespie } 181681090d4Sespie } 182681090d4Sespie return $result; 183681090d4Sespie} 184681090d4Sespie 185039cbdaaSespiesub process_parameters($self, $state) 186a409537dSespie{ 187681090d4Sespie my $inst = $state->repo->installed; 1887096cf21Sespie 189fef35309Sespie if (@ARGV == 0) { 19053a284c3Sespie if (!($state->{automatic} || $state->{exclude})) { 191c864de0cSdv $state->usage("No packages to delete"); 192a409537dSespie } 193a409537dSespie } else { 194681090d4Sespie for my $pkgname (@ARGV) { 195681090d4Sespie my $l; 196681090d4Sespie 197681090d4Sespie if (OpenBSD::PackageName::is_stem($pkgname)) { 198fef35309Sespie $l = $state->stem2location($inst, $pkgname, 199fef35309Sespie $state); 200681090d4Sespie } else { 201db099e94Sespie $l = $inst->find($pkgname); 202681090d4Sespie } 203681090d4Sespie if (!defined $l) { 204295c9761Sespie unless ($state->{exclude}) { 20545d818e9Sespie $state->say("Problem finding #1", 20645d818e9Sespie $pkgname); 207a409537dSespie $state->{bad}++; 208295c9761Sespie } 209681090d4Sespie } else { 210681090d4Sespie $self->add_location($state, $l); 211a409537dSespie } 212a409537dSespie } 213681090d4Sespie } 214a409537dSespie} 215a409537dSespie 216039cbdaaSespiesub finish_display($, $) 217a409537dSespie{ 218a409537dSespie} 219a409537dSespie 220039cbdaaSespiesub really_remove($set, $state) 221681090d4Sespie{ 222681090d4Sespie if ($state->{not}) { 223681090d4Sespie $state->status->what("Pretending to delete"); 224681090d4Sespie } else { 225681090d4Sespie $state->status->what("Deleting"); 226681090d4Sespie } 227d2f781d7Sespie $set->setup_header($state); 228a0a2ed7eSespie for my $pkg ($set->older) { 229a0a2ed7eSespie $set->setup_header($state, $pkg); 230a0a2ed7eSespie $state->log->set_context('-'.$pkg->pkgname); 231a0a2ed7eSespie OpenBSD::Delete::delete_handle($pkg, $state); 232681090d4Sespie } 233681090d4Sespie $state->progress->next($state->ntogo); 234de512720Sespie $state->syslog("Removed #1", $set->print); 235681090d4Sespie} 236681090d4Sespie 237039cbdaaSespiesub delete_dependencies($state) 238f1ddee08Sespie{ 239f1ddee08Sespie if ($state->defines("dependencies")) { 240f1ddee08Sespie return 1; 241f1ddee08Sespie } 242fb75580aSespie return $state->confirm_defaults_to_no("Delete them as well"); 243f1ddee08Sespie} 244f1ddee08Sespie 245039cbdaaSespiesub fix_bad_dependencies($state) 246f1ddee08Sespie{ 247f1ddee08Sespie if ($state->defines("baddepend")) { 248f1ddee08Sespie return 1; 249f1ddee08Sespie } 250fb75580aSespie return $state->confirm_defaults_to_no("Delete anyway"); 251f1ddee08Sespie} 252f1ddee08Sespie 253039cbdaaSespiesub process_set($self, $set, $state) 254681090d4Sespie{ 255681090d4Sespie my $todo = {}; 256681090d4Sespie my $bad = {}; 257681090d4Sespie for my $pkgname ($set->older_names) { 258681090d4Sespie unless (is_installed($pkgname)) { 259681090d4Sespie $state->errsay("#1 was not installed", $pkgname); 260681090d4Sespie $set->{finished} = 1; 261681090d4Sespie $set->cleanup(OpenBSD::Handle::NOT_FOUND); 262681090d4Sespie $state->{bad}++; 263681090d4Sespie return (); 264681090d4Sespie } 265681090d4Sespie my $r = OpenBSD::RequiredBy->new($pkgname); 266681090d4Sespie for my $pkg ($r->list) { 267a0a2ed7eSespie next if $set->{older}{$pkg}; 268681090d4Sespie my $f = $state->tracker->find($pkg); 269681090d4Sespie if (defined $f) { 270681090d4Sespie $todo->{$pkg} = $f; 271681090d4Sespie } else { 272681090d4Sespie $bad->{$pkg} = 1; 273681090d4Sespie } 274681090d4Sespie } 275681090d4Sespie } 276681090d4Sespie if (keys %$bad > 0) { 277f1ddee08Sespie my $bad2 = {}; 278f1ddee08Sespie for my $pkg (keys %$bad) { 279f1ddee08Sespie if (!is_installed($pkg)) { 280f1ddee08Sespie $bad2->{$pkg} = 1; 281f1ddee08Sespie } 282f1ddee08Sespie } 283f1ddee08Sespie if (keys %$bad2 > 0) { 2846ba149ccSphessler $state->errsay("#1 depends on non-existent #2", 285303a35c3Sespie $set->print, join(' ', sort keys %$bad2)); 286f1ddee08Sespie if (fix_bad_dependencies($state)) { 287f1ddee08Sespie for my $pkg (keys %$bad2) { 288f1ddee08Sespie delete $bad->{$pkg}; 289f1ddee08Sespie } 290f1ddee08Sespie } 291f1ddee08Sespie } 292f1ddee08Sespie } 293*ab16e3ccSespie # that's where I should check for alternates in bad 294f1ddee08Sespie if (keys %$bad > 0) { 295fef35309Sespie if (!$state->{do_automatic} || $state->verbose) { 296681090d4Sespie $state->errsay("can't delete #1 without deleting #2", 297303a35c3Sespie $set->print, join(' ', sort keys %$bad)); 29830df58edSespie } 299fef35309Sespie if (!$state->{do_automatic}) { 300f1ddee08Sespie if (delete_dependencies($state)) { 301681090d4Sespie my $l = create_locations($state, keys %$bad); 302716bc83bSespie $state->tracker->todo(@$l); 303681090d4Sespie return (@$l, $set); 304681090d4Sespie } 305681090d4Sespie $state->{bad}++; 306681090d4Sespie } 307681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 30846412527Sespie $state->tracker->cant($set); 309681090d4Sespie return (); 310681090d4Sespie } 311681090d4Sespie # XXX this si where we should detect loops 312681090d4Sespie if (keys %$todo > 0) { 313681090d4Sespie if ($set->{once}) { 314681090d4Sespie for my $set2 (values %$todo) { 315681090d4Sespie # XXX merge all ? 316681090d4Sespie $set->add_older($set2->older); 317681090d4Sespie $set2->{merged} = $set; 318681090d4Sespie $set2->{finished} = 1; 319681090d4Sespie } 320681090d4Sespie delete $set->{once}; 321681090d4Sespie return ($set); 322681090d4Sespie } 323681090d4Sespie $set->{once} = 1; 324d2f781d7Sespie $state->build_deptree($set, values %$todo); 325681090d4Sespie return (values %$todo, $set); 326681090d4Sespie } 327681090d4Sespie for my $pkg ($set->older) { 328681090d4Sespie $pkg->complete_old; 329ab0cb692Sespie if (!defined $pkg->plist) { 330ab0cb692Sespie $state->say("Corrupt set #1, run pkg_check", 331ab0cb692Sespie $set->print); 332ab0cb692Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 333ab0cb692Sespie $state->tracker->cant($set); 334ab0cb692Sespie return (); 335ab0cb692Sespie } 336a0a2ed7eSespie if ($state->{do_automatic} && 337a0a2ed7eSespie $pkg->plist->has('manual-installation')) { 33830df58edSespie $state->say("Won't delete manually installed #1", 339303a35c3Sespie $set->print) if $state->verbose; 340681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 34146412527Sespie $state->tracker->cant($set); 342681090d4Sespie return (); 343681090d4Sespie } 344e954bb43Sespie if (defined $pkg->plist->{tags}) { 345e954bb43Sespie if (!$set->solver->solve_tags($state)) { 3467f38ef04Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 347e954bb43Sespie $state->tracker->cant($set); 348e954bb43Sespie return (); 349e954bb43Sespie } 350e954bb43Sespie } 351e954bb43Sespie } 352681090d4Sespie really_remove($set, $state); 35346412527Sespie $set->cleanup; 35446412527Sespie $state->tracker->done($set); 355681090d4Sespie return (); 356681090d4Sespie} 357681090d4Sespie 358039cbdaaSespiesub main($self, $state) 359a409537dSespie{ 360bd849d19Sespie if ($state->{exclude}) { 361bd849d19Sespie my $names = {}; 362bd849d19Sespie for my $l (@{$state->{setlist}}) { 363bd849d19Sespie for my $n ($l->older_names) { 364bd849d19Sespie $names->{$n} = 1; 365bd849d19Sespie } 366bd849d19Sespie } 367bd849d19Sespie $state->{setlist} = []; 368bd849d19Sespie my $inst = $state->repo->installed; 369bd849d19Sespie for my $l (@{$inst->locations_list}) { 370bd849d19Sespie $self->add_location($state, $l) if !$names->{$l->name}; 371bd849d19Sespie } 372bd849d19Sespie } 373fef35309Sespie if ($state->{automatic}) { 374c487fac2Sespie if (!defined $state->{setlist}) { 375fef35309Sespie my $inst = $state->repo->installed; 376fef35309Sespie for my $l (@{$inst->locations_list}) { 377fef35309Sespie $self->add_location($state, $l); 378fef35309Sespie } 3794809484aSespie } 380fef35309Sespie $state->{do_automatic} = 1; 381fef35309Sespie $self->process_setlist($state); 3824809484aSespie } else { 3834809484aSespie $self->process_setlist($state); 384fef35309Sespie } 385a409537dSespie} 386a409537dSespie 387039cbdaaSespiesub new_state($self, $cmd) 388a409537dSespie{ 3897e83eca3Sespie return OpenBSD::PkgDelete::State->new($cmd); 390a409537dSespie} 391a409537dSespie 392a409537dSespie1; 393