1a409537dSespie#!/usr/bin/perl 2a409537dSespie# ex:ts=8 sw=4: 3*c487fac2Sespie# $OpenBSD: PkgDelete.pm,v 1.26 2011/11/25 23:58:40 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 24681090d4Sespiepackage OpenBSD::PkgDelete::Tracker; 25681090d4Sespie 26681090d4Sespiesub new 27681090d4Sespie{ 28681090d4Sespie my $class = shift; 29681090d4Sespie bless {}, $class; 30681090d4Sespie} 31681090d4Sespie 32681090d4Sespiesub sets_todo 33681090d4Sespie{ 34681090d4Sespie my ($self, $offset) = @_; 35681090d4Sespie return sprintf("%u/%u", (scalar keys %{$self->{done}})-$offset, 36681090d4Sespie scalar keys %{$self->{total}}); 37681090d4Sespie} 38681090d4Sespie 39681090d4Sespiesub handle_set 40681090d4Sespie{ 41681090d4Sespie my ($self, $set) = @_; 42681090d4Sespie $self->{total}{$set} = 1; 43681090d4Sespie if ($set->{finished}) { 44681090d4Sespie $self->{done}{$set} = 1; 45681090d4Sespie } 46681090d4Sespie} 47681090d4Sespie 48681090d4Sespiesub todo 49681090d4Sespie{ 5046412527Sespie my ($self, @list) = @_; 5146412527Sespie for my $set (@list) { 52681090d4Sespie for my $pkgname ($set->older_names) { 53681090d4Sespie $self->{todo}{$pkgname} = $set; 54681090d4Sespie } 55681090d4Sespie $self->handle_set($set); 56681090d4Sespie } 57681090d4Sespie} 58681090d4Sespie 59681090d4Sespie 60681090d4Sespiesub done 61681090d4Sespie{ 62681090d4Sespie my ($self, $set) = @_; 63681090d4Sespie $set->{finished} = 1; 64681090d4Sespie for my $pkgname ($set->older_names) { 65681090d4Sespie delete $self->{todo}{$pkgname}; 66681090d4Sespie } 67681090d4Sespie $self->handle_set($set); 68681090d4Sespie} 69681090d4Sespie 7046412527Sespiesub cant 7146412527Sespie{ 7246412527Sespie &done; 7346412527Sespie} 74681090d4Sespiesub find 75681090d4Sespie{ 76681090d4Sespie my ($self, $pkgname) = @_; 77681090d4Sespie return $self->{todo}{$pkgname}; 78681090d4Sespie} 79681090d4Sespie 80681090d4Sespie 81681090d4Sespie 82a409537dSespiepackage OpenBSD::PkgDelete::State; 83a409537dSespieour @ISA = qw(OpenBSD::AddDelete::State); 84a409537dSespie 85681090d4Sespiesub new 86681090d4Sespie{ 87681090d4Sespie my $class = shift; 88681090d4Sespie my $self = $class->SUPER::new(@_); 89681090d4Sespie $self->{tracker} = OpenBSD::PkgDelete::Tracker->new; 90681090d4Sespie return $self; 91681090d4Sespie} 92681090d4Sespie 93681090d4Sespiesub tracker 94681090d4Sespie{ 95681090d4Sespie my $self = shift; 96681090d4Sespie return $self->{tracker}; 97681090d4Sespie} 98681090d4Sespie 998a660df1Sespiesub handle_options 1008a660df1Sespie{ 1018a660df1Sespie my $state = shift; 1028a660df1Sespie $state->SUPER::handle_options('', 10367515decSespie '[-acinqsvx] [-B pkg-destdir] [-D name[=value]] pkg-name [...]'); 1048a660df1Sespie 1058a660df1Sespie my $base = $state->opt('B') // $ENV{'PKG_DESTDIR'} // ''; 1068a660df1Sespie if ($base ne '') { 1078a660df1Sespie $base.='/' unless $base =~ m/\/$/o; 1088a660df1Sespie } 1098a660df1Sespie $ENV{'PKG_DESTDIR'} = $base; 1108a660df1Sespie 1118a660df1Sespie $state->{destdir} = $base; 1128a660df1Sespie if ($base eq '') { 1138a660df1Sespie $state->{destdirname} = ''; 1148a660df1Sespie } else { 1158a660df1Sespie $state->{destdirname} = '${PKG_DESTDIR}'; 1168a660df1Sespie } 1178a660df1Sespie} 1188a660df1Sespie 119681090d4Sespiesub stem2location 120681090d4Sespie{ 121681090d4Sespie my ($self, $locator, $name, $state) = @_; 122681090d4Sespie require OpenBSD::Search; 123681090d4Sespie my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name)); 124681090d4Sespie if (@$l > 1 && !$state->defines('allversions')) { 125681090d4Sespie $l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l); 126681090d4Sespie } 127681090d4Sespie return $state->choose_location($name, $l); 128a409537dSespie} 129a409537dSespie 130303a35c3Sespiesub deleteset 131303a35c3Sespie{ 132303a35c3Sespie my $self = shift; 133303a35c3Sespie require OpenBSD::UpdateSet; 134303a35c3Sespie 135303a35c3Sespie return OpenBSD::DeleteSet->new($self); 136303a35c3Sespie} 137303a35c3Sespie 138303a35c3Sespiesub deleteset_from_location 139303a35c3Sespie{ 140303a35c3Sespie my ($self, $location) = @_; 141303a35c3Sespie return $self->deleteset->add_older(OpenBSD::Handle->from_location($location)); 142303a35c3Sespie} 143303a35c3Sespie 144d2f781d7Sespiepackage OpenBSD::DeleteSet; 145d2f781d7Sespiesub setup_header 146d2f781d7Sespie{ 147d2f781d7Sespie my ($set, $state, $handle) = @_; 148d2f781d7Sespie my $header = $state->deptree_header($set); 149d2f781d7Sespie if (defined $handle) { 150d2f781d7Sespie $header .= $handle->pkgname; 151d2f781d7Sespie } else { 152d2f781d7Sespie $header .= $set->print; 153d2f781d7Sespie } 154d2f781d7Sespie if (!$state->progress->set_header($header)) { 155d2f781d7Sespie return unless $state->verbose; 156d2f781d7Sespie $header = "Deleting $header"; 157d2f781d7Sespie if (defined $state->{lastheader} && 158d2f781d7Sespie $header eq $state->{lastheader}) { 159d2f781d7Sespie return; 160d2f781d7Sespie } 161d2f781d7Sespie $state->{lastheader} = $header; 162d2f781d7Sespie $state->print("#1", $header); 163d2f781d7Sespie $state->print("(pretending) ") if $state->{not}; 164d2f781d7Sespie if ($state->{do_faked}) { 165d2f781d7Sespie $state->print(" under #1", $state->{destdir}); 166d2f781d7Sespie } 167d2f781d7Sespie $state->print("\n"); 168d2f781d7Sespie } 169d2f781d7Sespie} 170d2f781d7Sespie 171a409537dSespiepackage OpenBSD::PkgDelete; 172a409537dSespieour @ISA = qw(OpenBSD::AddDelete); 173a409537dSespie 174a409537dSespieuse OpenBSD::PackingList; 175a409537dSespieuse OpenBSD::RequiredBy; 176a409537dSespieuse OpenBSD::Delete; 177a409537dSespieuse OpenBSD::PackageInfo; 178a409537dSespieuse OpenBSD::UpdateSet; 179681090d4Sespieuse OpenBSD::Handle; 180a409537dSespie 181a409537dSespie 182681090d4Sespiesub add_location 183681090d4Sespie{ 184681090d4Sespie my ($self, $state, $l) = @_; 185681090d4Sespie push(@{$state->{setlist}}, 186303a35c3Sespie $state->deleteset_from_location($l)); 187681090d4Sespie} 188681090d4Sespie 189681090d4Sespiesub create_locations 190681090d4Sespie{ 191681090d4Sespie my ($state, @l) = @_; 192681090d4Sespie my $inst = $state->repo->installed; 193681090d4Sespie my $result = []; 194681090d4Sespie for my $name (@l) { 195681090d4Sespie my $l = $inst->find($name, $state->{arch}); 196681090d4Sespie if (!defined $l) { 197681090d4Sespie $state->errsay("Can't find #1 in installed packages", 198681090d4Sespie $name); 199681090d4Sespie $state->{bad}++; 200681090d4Sespie } else { 201303a35c3Sespie push(@$result, $state->deleteset_from_location($l)); 202681090d4Sespie } 203681090d4Sespie } 204681090d4Sespie return $result; 205681090d4Sespie} 206681090d4Sespie 207a409537dSespiesub process_parameters 208a409537dSespie{ 209a409537dSespie my ($self, $state) = @_; 2107096cf21Sespie 211681090d4Sespie my $inst = $state->repo->installed; 2127096cf21Sespie 213fef35309Sespie if (@ARGV == 0) { 214fef35309Sespie if (!$state->{automatic}) { 215fef35309Sespie $state->fatal("No packages to delete"); 216a409537dSespie } 217a409537dSespie } else { 218681090d4Sespie for my $pkgname (@ARGV) { 219681090d4Sespie my $l; 220681090d4Sespie 221681090d4Sespie if (OpenBSD::PackageName::is_stem($pkgname)) { 222fef35309Sespie $l = $state->stem2location($inst, $pkgname, 223fef35309Sespie $state); 224681090d4Sespie } else { 225db099e94Sespie $l = $inst->find($pkgname); 226681090d4Sespie } 227681090d4Sespie if (!defined $l) { 228681090d4Sespie $state->say("Problem finding #1", $pkgname); 229a409537dSespie $state->{bad}++; 230681090d4Sespie } else { 231681090d4Sespie $self->add_location($state, $l); 232a409537dSespie } 233a409537dSespie } 234681090d4Sespie } 235a409537dSespie} 236a409537dSespie 237a409537dSespiesub finish_display 238a409537dSespie{ 239a409537dSespie} 240a409537dSespie 241681090d4Sespiesub really_remove 242681090d4Sespie{ 243681090d4Sespie my ($set, $state) = @_; 244681090d4Sespie if ($state->{not}) { 245681090d4Sespie $state->status->what("Pretending to delete"); 246681090d4Sespie } else { 247681090d4Sespie $state->status->what("Deleting"); 248681090d4Sespie } 249d2f781d7Sespie $set->setup_header($state); 250681090d4Sespie for my $pkgname ($set->older_names) { 251d2f781d7Sespie $set->setup_header($state, $set->{older}{$pkgname}); 252681090d4Sespie $state->log->set_context('-'.$pkgname); 253681090d4Sespie OpenBSD::Delete::delete_package($pkgname, $state); 254681090d4Sespie } 255681090d4Sespie $state->progress->next($state->ntogo); 256de512720Sespie $state->syslog("Removed #1", $set->print); 257681090d4Sespie} 258681090d4Sespie 259f1ddee08Sespiesub delete_dependencies 260f1ddee08Sespie{ 261f1ddee08Sespie my $state = shift; 262f1ddee08Sespie 263f1ddee08Sespie if ($state->defines("dependencies")) { 264f1ddee08Sespie return 1; 265f1ddee08Sespie } 266f1ddee08Sespie if ($state->{interactive}) { 267f1ddee08Sespie return $state->confirm("Delete them as well", 0); 268f1ddee08Sespie } 269f1ddee08Sespie return 0; 270f1ddee08Sespie} 271f1ddee08Sespie 272f1ddee08Sespiesub fix_bad_dependencies 273f1ddee08Sespie{ 274f1ddee08Sespie my $state = shift; 275f1ddee08Sespie 276f1ddee08Sespie if ($state->defines("baddepend")) { 277f1ddee08Sespie return 1; 278f1ddee08Sespie } 279f1ddee08Sespie if ($state->{interactive}) { 280f1ddee08Sespie return $state->confirm("Delete anyways", 0); 281f1ddee08Sespie } 282f1ddee08Sespie return 0; 283f1ddee08Sespie} 284f1ddee08Sespie 28546412527Sespiesub process_set 286681090d4Sespie{ 28746412527Sespie my ($self, $set, $state) = @_; 288681090d4Sespie 289681090d4Sespie my $todo = {}; 290681090d4Sespie my $bad = {}; 291681090d4Sespie for my $pkgname ($set->older_names) { 292681090d4Sespie unless (is_installed($pkgname)) { 293681090d4Sespie $state->errsay("#1 was not installed", $pkgname); 294681090d4Sespie $set->{finished} = 1; 295681090d4Sespie $set->cleanup(OpenBSD::Handle::NOT_FOUND); 296681090d4Sespie $state->{bad}++; 297681090d4Sespie return (); 298681090d4Sespie } 299681090d4Sespie my $r = OpenBSD::RequiredBy->new($pkgname); 300681090d4Sespie for my $pkg ($r->list) { 301681090d4Sespie next if $set->{older}->{$pkg}; 302681090d4Sespie my $f = $state->tracker->find($pkg); 303681090d4Sespie if (defined $f) { 304681090d4Sespie $todo->{$pkg} = $f; 305681090d4Sespie } else { 306681090d4Sespie $bad->{$pkg} = 1; 307681090d4Sespie } 308681090d4Sespie } 309681090d4Sespie } 310681090d4Sespie if (keys %$bad > 0) { 311f1ddee08Sespie my $bad2 = {}; 312f1ddee08Sespie for my $pkg (keys %$bad) { 313f1ddee08Sespie if (!is_installed($pkg)) { 314f1ddee08Sespie $bad2->{$pkg} = 1; 315f1ddee08Sespie } 316f1ddee08Sespie } 317f1ddee08Sespie if (keys %$bad2 > 0) { 318f1ddee08Sespie $state->errsay("#1 depends on non-existant #2", 319303a35c3Sespie $set->print, join(' ', sort keys %$bad2)); 320f1ddee08Sespie if (fix_bad_dependencies($state)) { 321f1ddee08Sespie for my $pkg (keys %$bad2) { 322f1ddee08Sespie delete $bad->{$pkg}; 323f1ddee08Sespie } 324f1ddee08Sespie } 325f1ddee08Sespie } 326f1ddee08Sespie } 327f1ddee08Sespie if (keys %$bad > 0) { 328fef35309Sespie if (!$state->{do_automatic} || $state->verbose) { 329681090d4Sespie $state->errsay("can't delete #1 without deleting #2", 330303a35c3Sespie $set->print, join(' ', sort keys %$bad)); 33130df58edSespie } 332fef35309Sespie if (!$state->{do_automatic}) { 333f1ddee08Sespie if (delete_dependencies($state)) { 334681090d4Sespie my $l = create_locations($state, keys %$bad); 335716bc83bSespie $state->tracker->todo(@$l); 336681090d4Sespie return (@$l, $set); 337681090d4Sespie } 338681090d4Sespie $state->{bad}++; 339681090d4Sespie } 340681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 34146412527Sespie $state->tracker->cant($set); 342681090d4Sespie return (); 343681090d4Sespie } 344681090d4Sespie # XXX this si where we should detect loops 345681090d4Sespie if (keys %$todo > 0) { 346681090d4Sespie if ($set->{once}) { 347681090d4Sespie for my $set2 (values %$todo) { 348681090d4Sespie # XXX merge all ? 349681090d4Sespie $set->add_older($set2->older); 350681090d4Sespie $set2->{merged} = $set; 351681090d4Sespie $set2->{finished} = 1; 352681090d4Sespie } 353681090d4Sespie delete $set->{once}; 354681090d4Sespie return ($set); 355681090d4Sespie } 356681090d4Sespie $set->{once} = 1; 357d2f781d7Sespie $state->build_deptree($set, values %$todo); 358681090d4Sespie return (values %$todo, $set); 359681090d4Sespie } 360fef35309Sespie if ($state->{do_automatic}) { 361681090d4Sespie for my $pkg ($set->older) { 362681090d4Sespie $pkg->complete_old; 363681090d4Sespie if ($pkg->plist->has('manual-installation')) { 36430df58edSespie $state->say("Won't delete manually installed #1", 365303a35c3Sespie $set->print) if $state->verbose; 366681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 36746412527Sespie $state->tracker->cant($set); 368681090d4Sespie return (); 369681090d4Sespie } 370681090d4Sespie } 371681090d4Sespie } 372681090d4Sespie really_remove($set, $state); 37346412527Sespie $set->cleanup; 37446412527Sespie $state->tracker->done($set); 375681090d4Sespie return (); 376681090d4Sespie} 377681090d4Sespie 378a409537dSespiesub main 379a409537dSespie{ 380a409537dSespie my ($self, $state) = @_; 381a409537dSespie 382fef35309Sespie if ($state->{automatic}) { 383*c487fac2Sespie if (!defined $state->{setlist}) { 384fef35309Sespie my $inst = $state->repo->installed; 385fef35309Sespie for my $l (@{$inst->locations_list}) { 386fef35309Sespie $self->add_location($state, $l); 387fef35309Sespie } 3884809484aSespie } 389fef35309Sespie $state->{do_automatic} = 1; 390fef35309Sespie $self->process_setlist($state); 3914809484aSespie } else { 3924809484aSespie $self->process_setlist($state); 393fef35309Sespie } 394a409537dSespie} 395a409537dSespie 396a409537dSespiesub new_state 397a409537dSespie{ 3987e83eca3Sespie my ($self, $cmd) = @_; 3997e83eca3Sespie return OpenBSD::PkgDelete::State->new($cmd); 400a409537dSespie} 401a409537dSespie 402a409537dSespie1; 403