#!/usr/bin/perl # ex:ts=8 sw=4: # $OpenBSD: pkg_delete,v 1.96 2007/04/15 10:17:29 espie Exp $ # # Copyright (c) 2003-2007 Marc Espie # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. use strict; use warnings; use OpenBSD::Getopt; use OpenBSD::PackingList; use OpenBSD::PackingOld; use OpenBSD::RequiredBy; use OpenBSD::Error; use OpenBSD::ProgressMeter; use OpenBSD::Interactive; use OpenBSD::Delete; use OpenBSD::PackageInfo; our %forced = (); our $not; set_usage('pkg_delete [-cIinqvx] [-B pkg-destdir] [-F keywords] pkg-name [...]'); our ($opt_v, $opt_D, $opt_d, $opt_n, $opt_q, $opt_p, $opt_c, $opt_L, $opt_B, $opt_I, $opt_i, $opt_x); $opt_v = 0; try { getopts('vchixDdnf:F:qpS:L:B:I', {'v' => sub {++$opt_v;}, 'h' => sub { Usage(); }, 'F' => sub { for my $o (split/,/, shift) { $forced{$o} = 1; } }, 'f' => sub { for my $o (split/,/, shift) { $forced{$o} = 1; } }}); } catchall { Usage($_); }; if ($opt_D) { $opt_I = 1; } $opt_B = $ENV{'PKG_DESTDIR'} unless defined $opt_B; $opt_B = '' unless defined $opt_B; if ($opt_B ne '') { $opt_B.='/' unless $opt_B =~ m/\/$/; } $ENV{'PKG_DESTDIR'} = $opt_B; $opt_L = '/usr/local' unless defined $opt_L; if (defined $opt_p) { Usage "Option p is obsolete"; } if (defined $opt_d) { Usage "Option d is obsolete"; } my %done; my $removed; try { my $state = new OpenBSD::Error; $state->{not} = $opt_n; # XXX RequiredBy $not = $opt_n; $state->{quick} = $opt_q; $state->{verbose} = $opt_v >= 2; $state->{very_verbose} = $opt_v; $state->{beverbose} = $opt_n || ($opt_v >= 2); $state->{extra} = $opt_c; $state->{dont_run_scripts} = $opt_I; $state->{forced} = \%forced; $state->{destdir} = $opt_B; $state->{interactive} = $opt_i; if ($opt_B eq '') { $state->{destdirname} = ''; } else { $state->{destdirname} = '${PKG_DESTDIR}'; } $ENV{'PKG_DELETE_EXTRA'} = $state->{extra} ? "Yes" : "No"; lock_db($opt_n); if (!$opt_x && !$state->{beverbose}) { OpenBSD::ProgressMeter::enable(); } if ($< && !$forced{nonroot}) { if ($state->{not}) { Warn "$0 should be run as root\n"; } else { Fatal "$0 must be run as root"; } } # First, resolve pkg names my @realnames; my $bad; OpenBSD::PackageInfo::solve_installed_names(\@ARGV, \@realnames, "(removing them all)", $state); my @todo = OpenBSD::RequiredBy->compute_closure(@realnames); if (@todo > @realnames) { my $details = $state->{very_verbose} || $forced{verbosedeps}; my $show = sub { my ($p, $d) = @_; print "Can't remove ".join(' ', @$p)." without also removing:\n" .join(' ', @$d)."\n"; }; if ($state->{interactive} || !$details) { my %deps = map {($_, 1)} @todo; for my $p (@realnames) { delete $deps{$p}; } &$show([@realnames], [keys %deps]); if ((keys %deps) > 1 && OpenBSD::Interactive::confirm( "Do you want details", $state->{interactive}, 1)) { $details = 1; } } if ($details) { for my $pkg (@realnames) { my @deps = OpenBSD::RequiredBy->compute_closure($pkg); next unless @deps > 1; @deps = grep {$_ ne $pkg} @deps; &$show([$pkg], [@deps]); } } my $them = @todo > 1 ? 'them' : 'it'; if ($forced{dependencies} or OpenBSD::Interactive::confirm("Do you want to remove $them as well", $state->{interactive}, 0)) { print "(removing $them as well)\n"; } else { $bad = 1; } } if ($bad) { exit(1); } eval { # and finally, handle the removal { do { $removed = 0; DELETE: for my $pkgname (@todo) { next if $done{$pkgname}; unless (is_installed($pkgname)) { print "$pkgname was not installed\n"; $done{$pkgname} = 1; $removed++; next; } my $r = OpenBSD::RequiredBy->new($pkgname); if ($r->list() > 0) { if ($forced{baddepend}) { for my $p ($r->list()) { if ($done{$p}) { $r->delete($p); } else { next DELETE; } } } else { next; } } if (!OpenBSD::ProgressMeter::set_header($pkgname)) { print $opt_n ? "Pretending to delete " : "Deleting ", "$pkgname\n"; } $state->set_pkgname($pkgname); OpenBSD::Delete::delete_package($pkgname, $state); delete_installed($pkgname); $done{$pkgname} = 1; $removed++; } } while ($removed); } }; my $dielater = $@; OpenBSD::PackingElement::Lib::ensure_ldconfig($state); # delayed directory/user/group removal if (defined $state->{dirs_to_rm} or defined $state->{users_to_rm} or defined $state->{groups_to_rm}) { require OpenBSD::SharedItems; OpenBSD::SharedItems::cleanup($state) unless $state->{not}; } OpenBSD::PackingElement::Fontdir::finish_fontdirs($state); if ($state->{beverbose}) { OpenBSD::Vstat::tally(); } $state->delayed_output(); rethrow $dielater; } catch { print STDERR "$0: $_\n"; exit(1); };