1a409537dSespie#!/usr/bin/perl 2a409537dSespie# ex:ts=8 sw=4: 3*45d818e9Sespie# $OpenBSD: PkgDelete.pm,v 1.31 2014/02/08 16:21:54 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 19a409537dSespieuse strict; 20a409537dSespieuse warnings; 21a409537dSespie 22a409537dSespieuse OpenBSD::AddDelete; 23a409537dSespie 24295c9761Sespie 25681090d4Sespiepackage OpenBSD::PkgDelete::Tracker; 26681090d4Sespie 27681090d4Sespiesub new 28681090d4Sespie{ 29681090d4Sespie my $class = shift; 30681090d4Sespie bless {}, $class; 31681090d4Sespie} 32681090d4Sespie 33681090d4Sespiesub sets_todo 34681090d4Sespie{ 35681090d4Sespie my ($self, $offset) = @_; 36681090d4Sespie return sprintf("%u/%u", (scalar keys %{$self->{done}})-$offset, 37681090d4Sespie scalar keys %{$self->{total}}); 38681090d4Sespie} 39681090d4Sespie 40681090d4Sespiesub handle_set 41681090d4Sespie{ 42681090d4Sespie my ($self, $set) = @_; 43681090d4Sespie $self->{total}{$set} = 1; 44681090d4Sespie if ($set->{finished}) { 45681090d4Sespie $self->{done}{$set} = 1; 46681090d4Sespie } 47681090d4Sespie} 48681090d4Sespie 49681090d4Sespiesub todo 50681090d4Sespie{ 5146412527Sespie my ($self, @list) = @_; 5246412527Sespie for my $set (@list) { 53681090d4Sespie for my $pkgname ($set->older_names) { 54681090d4Sespie $self->{todo}{$pkgname} = $set; 55681090d4Sespie } 56681090d4Sespie $self->handle_set($set); 57681090d4Sespie } 58681090d4Sespie} 59681090d4Sespie 60681090d4Sespie 61681090d4Sespiesub done 62681090d4Sespie{ 63681090d4Sespie my ($self, $set) = @_; 64681090d4Sespie $set->{finished} = 1; 65681090d4Sespie for my $pkgname ($set->older_names) { 66681090d4Sespie delete $self->{todo}{$pkgname}; 67681090d4Sespie } 68681090d4Sespie $self->handle_set($set); 69681090d4Sespie} 70681090d4Sespie 7146412527Sespiesub cant 7246412527Sespie{ 7346412527Sespie &done; 7446412527Sespie} 75681090d4Sespiesub find 76681090d4Sespie{ 77681090d4Sespie my ($self, $pkgname) = @_; 78681090d4Sespie return $self->{todo}{$pkgname}; 79681090d4Sespie} 80681090d4Sespie 81681090d4Sespie 82681090d4Sespie 83a409537dSespiepackage OpenBSD::PkgDelete::State; 84a409537dSespieour @ISA = qw(OpenBSD::AddDelete::State); 85a409537dSespie 86681090d4Sespiesub new 87681090d4Sespie{ 88681090d4Sespie my $class = shift; 89681090d4Sespie my $self = $class->SUPER::new(@_); 90681090d4Sespie $self->{tracker} = OpenBSD::PkgDelete::Tracker->new; 91681090d4Sespie return $self; 92681090d4Sespie} 93681090d4Sespie 94681090d4Sespiesub tracker 95681090d4Sespie{ 96681090d4Sespie my $self = shift; 97681090d4Sespie return $self->{tracker}; 98681090d4Sespie} 99681090d4Sespie 1008a660df1Sespiesub handle_options 1018a660df1Sespie{ 1028a660df1Sespie my $state = shift; 103bd849d19Sespie $state->SUPER::handle_options('X', 104661d6a5dSjmc '[-acimnqsvXx] [-B pkg-destdir] [-D name[=value]] pkg-name [...]'); 1058a660df1Sespie 1068a660df1Sespie my $base = $state->opt('B') // $ENV{'PKG_DESTDIR'} // ''; 1078a660df1Sespie if ($base ne '') { 1088a660df1Sespie $base.='/' unless $base =~ m/\/$/o; 1098a660df1Sespie } 1108a660df1Sespie $ENV{'PKG_DESTDIR'} = $base; 1118a660df1Sespie 1128a660df1Sespie $state->{destdir} = $base; 1138a660df1Sespie if ($base eq '') { 1148a660df1Sespie $state->{destdirname} = ''; 1158a660df1Sespie } else { 1168a660df1Sespie $state->{destdirname} = '${PKG_DESTDIR}'; 1178a660df1Sespie } 118bd849d19Sespie $state->{exclude} = $state->opt('X'); 1198a660df1Sespie} 1208a660df1Sespie 121681090d4Sespiesub stem2location 122681090d4Sespie{ 123681090d4Sespie my ($self, $locator, $name, $state) = @_; 124681090d4Sespie require OpenBSD::Search; 125681090d4Sespie my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name)); 126681090d4Sespie if (@$l > 1 && !$state->defines('allversions')) { 127681090d4Sespie $l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l); 128681090d4Sespie } 129681090d4Sespie return $state->choose_location($name, $l); 130a409537dSespie} 131a409537dSespie 132303a35c3Sespiesub deleteset 133303a35c3Sespie{ 134303a35c3Sespie my $self = shift; 135303a35c3Sespie require OpenBSD::UpdateSet; 136303a35c3Sespie 137303a35c3Sespie return OpenBSD::DeleteSet->new($self); 138303a35c3Sespie} 139303a35c3Sespie 140303a35c3Sespiesub deleteset_from_location 141303a35c3Sespie{ 142303a35c3Sespie my ($self, $location) = @_; 143303a35c3Sespie return $self->deleteset->add_older(OpenBSD::Handle->from_location($location)); 144303a35c3Sespie} 145303a35c3Sespie 146d2f781d7Sespiepackage OpenBSD::DeleteSet; 147d2f781d7Sespiesub setup_header 148d2f781d7Sespie{ 149d2f781d7Sespie my ($set, $state, $handle) = @_; 150d2f781d7Sespie my $header = $state->deptree_header($set); 151d2f781d7Sespie if (defined $handle) { 152d2f781d7Sespie $header .= $handle->pkgname; 153d2f781d7Sespie } else { 154d2f781d7Sespie $header .= $set->print; 155d2f781d7Sespie } 156d2f781d7Sespie if (!$state->progress->set_header($header)) { 157d2f781d7Sespie return unless $state->verbose; 158d2f781d7Sespie $header = "Deleting $header"; 159d2f781d7Sespie if (defined $state->{lastheader} && 160d2f781d7Sespie $header eq $state->{lastheader}) { 161d2f781d7Sespie return; 162d2f781d7Sespie } 163d2f781d7Sespie $state->{lastheader} = $header; 164d2f781d7Sespie $state->print("#1", $header); 165d2f781d7Sespie $state->print("(pretending) ") if $state->{not}; 166d2f781d7Sespie if ($state->{do_faked}) { 167d2f781d7Sespie $state->print(" under #1", $state->{destdir}); 168d2f781d7Sespie } 169d2f781d7Sespie $state->print("\n"); 170d2f781d7Sespie } 171d2f781d7Sespie} 172d2f781d7Sespie 173a409537dSespiepackage OpenBSD::PkgDelete; 174a409537dSespieour @ISA = qw(OpenBSD::AddDelete); 175a409537dSespie 176a409537dSespieuse OpenBSD::PackingList; 177a409537dSespieuse OpenBSD::RequiredBy; 178a409537dSespieuse OpenBSD::Delete; 179a409537dSespieuse OpenBSD::PackageInfo; 180a409537dSespieuse OpenBSD::UpdateSet; 181681090d4Sespieuse OpenBSD::Handle; 182a409537dSespie 183a409537dSespie 184681090d4Sespiesub add_location 185681090d4Sespie{ 186681090d4Sespie my ($self, $state, $l) = @_; 187681090d4Sespie push(@{$state->{setlist}}, 188303a35c3Sespie $state->deleteset_from_location($l)); 189681090d4Sespie} 190681090d4Sespie 191681090d4Sespiesub create_locations 192681090d4Sespie{ 193681090d4Sespie my ($state, @l) = @_; 194681090d4Sespie my $inst = $state->repo->installed; 195681090d4Sespie my $result = []; 196681090d4Sespie for my $name (@l) { 197681090d4Sespie my $l = $inst->find($name, $state->{arch}); 198681090d4Sespie if (!defined $l) { 199681090d4Sespie $state->errsay("Can't find #1 in installed packages", 200681090d4Sespie $name); 201681090d4Sespie $state->{bad}++; 202681090d4Sespie } else { 203303a35c3Sespie push(@$result, $state->deleteset_from_location($l)); 204681090d4Sespie } 205681090d4Sespie } 206681090d4Sespie return $result; 207681090d4Sespie} 208681090d4Sespie 209a409537dSespiesub process_parameters 210a409537dSespie{ 211a409537dSespie my ($self, $state) = @_; 2127096cf21Sespie 213681090d4Sespie my $inst = $state->repo->installed; 2147096cf21Sespie 215fef35309Sespie if (@ARGV == 0) { 21653a284c3Sespie if (!($state->{automatic} || $state->{exclude})) { 217fef35309Sespie $state->fatal("No packages to delete"); 218a409537dSespie } 219a409537dSespie } else { 220681090d4Sespie for my $pkgname (@ARGV) { 221681090d4Sespie my $l; 222681090d4Sespie 223681090d4Sespie if (OpenBSD::PackageName::is_stem($pkgname)) { 224fef35309Sespie $l = $state->stem2location($inst, $pkgname, 225fef35309Sespie $state); 226681090d4Sespie } else { 227db099e94Sespie $l = $inst->find($pkgname); 228681090d4Sespie } 229681090d4Sespie if (!defined $l) { 230295c9761Sespie unless ($state->{exclude}) { 231*45d818e9Sespie $state->say("Problem finding #1", 232*45d818e9Sespie $pkgname); 233a409537dSespie $state->{bad}++; 234295c9761Sespie } 235681090d4Sespie } else { 236681090d4Sespie $self->add_location($state, $l); 237a409537dSespie } 238a409537dSespie } 239681090d4Sespie } 240a409537dSespie} 241a409537dSespie 242a409537dSespiesub finish_display 243a409537dSespie{ 244a409537dSespie} 245a409537dSespie 246681090d4Sespiesub really_remove 247681090d4Sespie{ 248681090d4Sespie my ($set, $state) = @_; 249681090d4Sespie if ($state->{not}) { 250681090d4Sespie $state->status->what("Pretending to delete"); 251681090d4Sespie } else { 252681090d4Sespie $state->status->what("Deleting"); 253681090d4Sespie } 254d2f781d7Sespie $set->setup_header($state); 255681090d4Sespie for my $pkgname ($set->older_names) { 256d2f781d7Sespie $set->setup_header($state, $set->{older}{$pkgname}); 257681090d4Sespie $state->log->set_context('-'.$pkgname); 258681090d4Sespie OpenBSD::Delete::delete_package($pkgname, $state); 259681090d4Sespie } 260681090d4Sespie $state->progress->next($state->ntogo); 261de512720Sespie $state->syslog("Removed #1", $set->print); 262681090d4Sespie} 263681090d4Sespie 264f1ddee08Sespiesub delete_dependencies 265f1ddee08Sespie{ 266f1ddee08Sespie my $state = shift; 267f1ddee08Sespie 268f1ddee08Sespie if ($state->defines("dependencies")) { 269f1ddee08Sespie return 1; 270f1ddee08Sespie } 271f1ddee08Sespie if ($state->{interactive}) { 272f1ddee08Sespie return $state->confirm("Delete them as well", 0); 273f1ddee08Sespie } 274f1ddee08Sespie return 0; 275f1ddee08Sespie} 276f1ddee08Sespie 277f1ddee08Sespiesub fix_bad_dependencies 278f1ddee08Sespie{ 279f1ddee08Sespie my $state = shift; 280f1ddee08Sespie 281f1ddee08Sespie if ($state->defines("baddepend")) { 282f1ddee08Sespie return 1; 283f1ddee08Sespie } 284f1ddee08Sespie if ($state->{interactive}) { 285f1ddee08Sespie return $state->confirm("Delete anyways", 0); 286f1ddee08Sespie } 287f1ddee08Sespie return 0; 288f1ddee08Sespie} 289f1ddee08Sespie 29046412527Sespiesub process_set 291681090d4Sespie{ 29246412527Sespie my ($self, $set, $state) = @_; 293681090d4Sespie 294681090d4Sespie my $todo = {}; 295681090d4Sespie my $bad = {}; 296681090d4Sespie for my $pkgname ($set->older_names) { 297681090d4Sespie unless (is_installed($pkgname)) { 298681090d4Sespie $state->errsay("#1 was not installed", $pkgname); 299681090d4Sespie $set->{finished} = 1; 300681090d4Sespie $set->cleanup(OpenBSD::Handle::NOT_FOUND); 301681090d4Sespie $state->{bad}++; 302681090d4Sespie return (); 303681090d4Sespie } 304681090d4Sespie my $r = OpenBSD::RequiredBy->new($pkgname); 305681090d4Sespie for my $pkg ($r->list) { 306681090d4Sespie next if $set->{older}->{$pkg}; 307681090d4Sespie my $f = $state->tracker->find($pkg); 308681090d4Sespie if (defined $f) { 309681090d4Sespie $todo->{$pkg} = $f; 310681090d4Sespie } else { 311681090d4Sespie $bad->{$pkg} = 1; 312681090d4Sespie } 313681090d4Sespie } 314681090d4Sespie } 315681090d4Sespie if (keys %$bad > 0) { 316f1ddee08Sespie my $bad2 = {}; 317f1ddee08Sespie for my $pkg (keys %$bad) { 318f1ddee08Sespie if (!is_installed($pkg)) { 319f1ddee08Sespie $bad2->{$pkg} = 1; 320f1ddee08Sespie } 321f1ddee08Sespie } 322f1ddee08Sespie if (keys %$bad2 > 0) { 323f1ddee08Sespie $state->errsay("#1 depends on non-existant #2", 324303a35c3Sespie $set->print, join(' ', sort keys %$bad2)); 325f1ddee08Sespie if (fix_bad_dependencies($state)) { 326f1ddee08Sespie for my $pkg (keys %$bad2) { 327f1ddee08Sespie delete $bad->{$pkg}; 328f1ddee08Sespie } 329f1ddee08Sespie } 330f1ddee08Sespie } 331f1ddee08Sespie } 332f1ddee08Sespie if (keys %$bad > 0) { 333fef35309Sespie if (!$state->{do_automatic} || $state->verbose) { 334681090d4Sespie $state->errsay("can't delete #1 without deleting #2", 335303a35c3Sespie $set->print, join(' ', sort keys %$bad)); 33630df58edSespie } 337fef35309Sespie if (!$state->{do_automatic}) { 338f1ddee08Sespie if (delete_dependencies($state)) { 339681090d4Sespie my $l = create_locations($state, keys %$bad); 340716bc83bSespie $state->tracker->todo(@$l); 341681090d4Sespie return (@$l, $set); 342681090d4Sespie } 343681090d4Sespie $state->{bad}++; 344681090d4Sespie } 345681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 34646412527Sespie $state->tracker->cant($set); 347681090d4Sespie return (); 348681090d4Sespie } 349681090d4Sespie # XXX this si where we should detect loops 350681090d4Sespie if (keys %$todo > 0) { 351681090d4Sespie if ($set->{once}) { 352681090d4Sespie for my $set2 (values %$todo) { 353681090d4Sespie # XXX merge all ? 354681090d4Sespie $set->add_older($set2->older); 355681090d4Sespie $set2->{merged} = $set; 356681090d4Sespie $set2->{finished} = 1; 357681090d4Sespie } 358681090d4Sespie delete $set->{once}; 359681090d4Sespie return ($set); 360681090d4Sespie } 361681090d4Sespie $set->{once} = 1; 362d2f781d7Sespie $state->build_deptree($set, values %$todo); 363681090d4Sespie return (values %$todo, $set); 364681090d4Sespie } 365fef35309Sespie if ($state->{do_automatic}) { 366681090d4Sespie for my $pkg ($set->older) { 367681090d4Sespie $pkg->complete_old; 368681090d4Sespie if ($pkg->plist->has('manual-installation')) { 36930df58edSespie $state->say("Won't delete manually installed #1", 370303a35c3Sespie $set->print) if $state->verbose; 371681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 37246412527Sespie $state->tracker->cant($set); 373681090d4Sespie return (); 374681090d4Sespie } 375681090d4Sespie } 376681090d4Sespie } 377681090d4Sespie really_remove($set, $state); 37846412527Sespie $set->cleanup; 37946412527Sespie $state->tracker->done($set); 380681090d4Sespie return (); 381681090d4Sespie} 382681090d4Sespie 383a409537dSespiesub main 384a409537dSespie{ 385a409537dSespie my ($self, $state) = @_; 386a409537dSespie 387bd849d19Sespie if ($state->{exclude}) { 388bd849d19Sespie my $names = {}; 389bd849d19Sespie for my $l (@{$state->{setlist}}) { 390bd849d19Sespie for my $n ($l->older_names) { 391bd849d19Sespie $names->{$n} = 1; 392bd849d19Sespie } 393bd849d19Sespie } 394bd849d19Sespie $state->{setlist} = []; 395bd849d19Sespie my $inst = $state->repo->installed; 396bd849d19Sespie for my $l (@{$inst->locations_list}) { 397bd849d19Sespie $self->add_location($state, $l) if !$names->{$l->name}; 398bd849d19Sespie } 399bd849d19Sespie } 400fef35309Sespie if ($state->{automatic}) { 401c487fac2Sespie if (!defined $state->{setlist}) { 402fef35309Sespie my $inst = $state->repo->installed; 403fef35309Sespie for my $l (@{$inst->locations_list}) { 404fef35309Sespie $self->add_location($state, $l); 405fef35309Sespie } 4064809484aSespie } 407fef35309Sespie $state->{do_automatic} = 1; 408fef35309Sespie $self->process_setlist($state); 4094809484aSespie } else { 4104809484aSespie $self->process_setlist($state); 411fef35309Sespie } 412a409537dSespie} 413a409537dSespie 414a409537dSespiesub new_state 415a409537dSespie{ 4167e83eca3Sespie my ($self, $cmd) = @_; 4177e83eca3Sespie return OpenBSD::PkgDelete::State->new($cmd); 418a409537dSespie} 419a409537dSespie 420a409537dSespie1; 421