| Server IP : 82.208.35.60 / Your IP : 216.73.216.89 Web Server : Apache/2.4.55 (FreeBSD) OpenSSL/1.1.1q-freebsd PHP/7.3.31 System : FreeBSD server7.d2m.cz 12.4-RELEASE-p9 FreeBSD 12.4-RELEASE-p9 GENERIC amd64 User : studiokobylisy_cz ( 1008) PHP Version : 7.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /usr/ports/ports-mgmt/pkg_cutleaves/files/ |
Upload File : |
#!/usr/bin/perl
# $Id$
#
# Copyright (c) 2003 Stefan Walter <sw@gegenunendlich.de>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
# Interactive script for deinstalling "leaf" packages
#
# Syntax: pkg_cutleaves [-cgLlRVx]
# Options:
# -c: Show comments, too; only works with '-l' (ignored otherwise)
# -g: Generate exclude list from kept/installed leaf packages
# -L: Interpret exclude file as list of packages that *should* be installed
# -l: List leaf packages only, don't ask if they should be deinstalled
# -R: Autoprune new leaves
# -V: Visual mode, invoke $EDITOR
# -x: Honor exclude list in $excludefile
use File::Temp qw/ tempfile /;
use Getopt::Std;
use strict;
my $excludefile = "/usr/local/etc/pkg_leaves.exclude";
my @pkgdeinstall = (qw{/usr/local/sbin/pkg delete -y});
my @pkgquery = (qw{/usr/local/sbin/pkg query});
my $exclpattern;
my %leavestokeep;
my %opt;
getopts('cgLlRVx', \%opt);
set_excl_pattern();
# LIST MODE
if ($opt{l}) {
# Just print out the list of leaves, one per line
my %leaves = get_leaves();
foreach my $leaf (sort keys %leaves) {
if ($opt{c}) {
print "$leaf - $leaves{$leaf}\n";
}
else {
print "$leaf\n";
}
$leavestokeep{$leaf} = 1;
}
}
# LINT MODE
elsif ($opt{L}) {
my @excludes = get_excludelist();
my @pkgs = get_packages();
# For each installed package
for my $pkg (@pkgs) {
my ($file, $required) = @$pkg;
# Clobber any exclude patterns that match this package
for (my $i = 0; $i < @excludes; $i++) {
if ($file =~ /\Q$excludes[$i]\E/) {
splice(@excludes, $i--, 1);
}
}
# If matches exclude list and is not a leaf
if ($required && ($file =~ $exclpattern)) {
print "$file matches exclude list, but is not a leaf\n"
}
else {
$leavestokeep{$file} = 1;
}
}
# In exclude list, but not installed
# For each remaining entry in exclude list
foreach my $exclude (@excludes) {
print "Exclude pattern '$exclude' matches no installed packages\n";
}
}
# INTERACTIVE MODE
else {
my @cutleaves;
my ($i, $again);
# Get list of leaf packages and put them into a hash
my %leaves = get_leaves();
# Any leaves to work with?
my $nleaves = keys %leaves;
if ($nleaves > 0) {
# If we don't have superuser rights, notify the user.
if ($< != 0) {
print "You need to have root permissions for deinstalling packages.\n";
$again = 'n';
}
else {
$again = 'y';
}
}
else {
# If not, don't go on, there's nothing to do.
print "** Didn't find any leaves to work with, exiting.\n";
print "** If this is unexpected, check your exclude file, please.\n";
$again = 'n';
}
# Loop while the user wants to
ROUND: while($again ne 'n') {
# Always start with an empty list of leaves to cut
my %leavestocut;
# Initialize counter for progress status
$i = 1;
if ($opt{V}) {
# Write list, including descriptions out to temp file
my ($fh, $filename) = tempfile() or die "Can't create file: $!";
print $fh "# Delete the lines whose packages you want removed.\n";
print $fh "# Simply save and quit, when you are done.\n";
print $fh "# There are $nleaves (new) leaf packages:\n";
foreach my $leaf (sort keys %leaves) {
# XXX Calculate maximum length first instead of 40?
printf $fh "%-40s - %s\n", $leaf, $leaves{$leaf};
}
close($fh);
# Invoke $EDITOR, vi if no EDITOR set
my $editor = ($ENV{'EDITOR'} eq "") ? "vi" : $ENV{'EDITOR'};
system "$editor $filename";
if ($? == -1) {
print "failed to execute: $!\n";
unlink($filename);
exit 1;
}
# Read back and parse list, the diff to the original list will be removed
open($fh, $filename) or die "Can't reopen file: $!";
while(<$fh>) {
next if $_ =~ m/\s*#/;
next if $_ =~ m/^\s*$/;
my ($leaf, $desc) = split(/\s/, $_, 2);
if (defined $leaves{$leaf}) {
delete $leaves{$leaf};
$leavestokeep{$leaf} = 1;
} else {
print "Package \"$leaf\" not known, skipping\n";
}
}
close($fh);
unlink($filename);
exit 0 if (keys %leaves == 0);
# print the lists of packages which will be removed, due to
# typos in the editor, the user might have 'removed' a package
# he is still interested in. This batch mode, really should get a last
# confirmation from the user.
print "The following packages will be removed:\n";
# XXX Use map here?
# XXX check empty hash FIXME
foreach my $leaf (sort keys %leaves) {
$leavestocut{$leaf} = 1;
print " $leaf\n";
}
print "Are you sure? [yes]: ";
my ($answer) = (lc(<STDIN>) =~ /(\S)/o);
print "\n";
exit 0 if $answer =~ /n/;
# Fallthrough to actual package removal
} else {
LEAVESLOOP: foreach my $leaf (sort keys %leaves) {
print "Package $i of $nleaves:\n";
print "$leaf - $leaves{$leaf}\n";
print "$leaf - [keep]/(d)elete/(f)lush marked pkgs/(a)bort? ";
# Get first character of input, without leading whitespace
my ($answer) = (lc(<STDIN>) =~ m/(\S)/);
if ($answer eq 'd') {
print "** Marking $leaf for removal.\n\n";
$leavestocut{$leaf} = 1;
}
elsif ($answer eq 'f') {
print "\n";
last LEAVESLOOP;
}
elsif ($answer eq 'a') {
print "\n";
$opt{aborted} = 1;
last ROUND;
}
else {
print "** Keeping $leaf.\n\n";
$leavestokeep{$leaf} = 1;
}
$i++;
} # LEAVESLOOP
}
AUTOPRUNE: # The -R switch jump
# Initialize 'progress meter'
my $ncuts = keys %leavestocut;
my $noff = 0;
# loop through packages marked for removal and pkg_deinstall them
foreach my $leaf (sort keys %leavestocut) {
$noff++;
print "Deleting $leaf (package $noff of $ncuts).\n";
my @deinstall_args = (@pkgdeinstall, $leaf);
if ((my $status = system(@deinstall_args) >> 8) != 0) {
print STDERR "\n\n$0: pkg_deinstall returned $status - exiting, fix this first.\n\n";
last ROUND;
}
push @cutleaves, $leaf;
}
# Get new list of leaf packages and put them into a hash
%leaves = get_leaves();
# Ignore all leaves the user already told us to keep
foreach my $leaf (keys %leavestokeep) {
delete $leaves{$leaf}
}
# Any leaves left?
$nleaves = keys %leaves;
if ($nleaves == 0) {
# If not, don't go on, there's nothing left to do.
print "** Didn't find any new leaves to work with, exiting.\n";
last ROUND;
}
if ($opt{R}) { # start autopruning new leaves
print "\n** Autopruning new leaves (Ctrl-C now to stop!) **\n" x 2;
sleep 1;
%leavestocut = %leaves;
goto AUTOPRUNE;
} # AUTOPRUNE
print "Go on with new leaf packages ([yes]/no)? ";
# Get first character of input, without leading whitespace
($again) = (lc(<STDIN>) =~ /(\S)/o);
print "\n";
} # ROUND
# print list of removed packages, sorted lexically, and their number
print "** Deinstalled packages:\n";
foreach my $cutleaf (sort @cutleaves) {
print "$cutleaf\n";
}
my $noff = @cutleaves;
print "** Number of deinstalled packages: $noff\n";
}
# Generate exclude file
if ($opt{g}) {
if ($opt{aborted}) {
die "\n** Skipping exclude file generation on aborted session **\n";
}
if (-e $excludefile) {
print "\nExclude file ($excludefile) exists! Overwrite ((y)es/[no])? ";
my $answer = <STDIN>;
unless ($answer =~ /^y(es)?$/io) {
exit 0;
}
}
create_excludelist();
}
#
# Set the exclude pattern
#
sub set_excl_pattern {
my @excludes = get_excludelist();
$exclpattern = @excludes
? join('|', map{qr(\Q$_\E)} @excludes)
: ' '; # default non-exclusive
$exclpattern = qr{^($exclpattern)$}o;
}
#
# Read the exclude list if the file exists
# Parameter: path of the exclude file
#
sub get_excludelist {
my @excludelist;
# XXX: Don't check command line params in a subroutine
if (($opt{x} || $opt{L}) && -f $excludefile && -T $excludefile) {
open(EXCLFILE, $excludefile)
or die "Couldn't open $excludefile!";
while(my $exclude = <EXCLFILE>) {
chomp($exclude);
# Ignore comments and empty lines, add others to the list
unless ($exclude =~ /(^ *#)|(^ *$)/o) {
push(@excludelist, $exclude);
}
}
close(EXCLFILE)
or warn "Failed to close exclude file ($excludefile): $!\n";
}
return @excludelist;
}
#
# Return a list of all packages
#
sub get_packages {
my @pkgs;
open(PKGQUERY, '-|', @pkgquery, '-a', '%n\t%n-%v\t%?r\t%c')
or die "Couldn't read output from $pkgquery[0]!";
while (my $p = <PKGQUERY>) {
chomp($p);
push(@pkgs, [ split(/\t/, $p) ]);
}
close PKGQUERY;
return @pkgs;
}
#
# Get a hash (name => comment) of all leaves
#
sub get_leaves {
my %leaves;
my @pkgs = get_packages();
foreach my $pkg (@pkgs) {
my ($name, $file, $required, $comment) = @$pkg;
unless ($required) {
if ($name =~ $exclpattern) {
$leavestokeep{$file} = 1;
}
else {
unless($comment) {
$comment = 'No short description';
}
$leaves{$file} = $comment;
}
}
}
return %leaves;
}
#
# Write the list of exclusions to a file
#
sub create_excludelist {
open(EXCLFILE, ">$excludefile")
or die "Failed to open exclude list ($excludefile): $!\n";
print EXCLFILE '# Auto-generated exclude list ', scalar localtime, "\n";
for (sort keys %leavestokeep) {
/^(.+)-g?\d.*$/o
? print EXCLFILE $1, "\n"
: warn 'Unable to extract exclude pattern from ', $_, "\n";
}
print "New exclude list ($excludefile) generated.\n";
}