Created
May 7, 2026 21:10
-
-
Save Ropid/0d9a753d8c69c21e4e25cc6819eadec9 to your computer and use it in GitHub Desktop.
list running programs that are using deleted files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/perl | |
| use strict; | |
| use warnings FATAL => 'all'; | |
| use 5.020; | |
| # use Data::Dumper qw'Dumper'; | |
| # use List::Util qw'any none'; | |
| use POSIX qw'isatty'; | |
| my ($program_name) = $0 =~ m{(?:.*/)?(.*)}; | |
| ## search paths | |
| my $whitelist = qr{ | |
| ^/usr/ | | |
| ^/opt/ | |
| }x; | |
| my $blacklist = qr{ | |
| [.]cache$ | | |
| \Q.cache (deleted)\E$ | | |
| /gschemas[.]compiled$ | | |
| ^/usr/share/locale/ | |
| }x; | |
| ## hash with ANSI color escape codes; empty values if not running in a terminal | |
| my %C; | |
| for ("r", "a0".."a7", "b0".."b7") { | |
| $C{$_} = ""; | |
| } | |
| if (isatty *STDOUT) { | |
| for (keys %C) { | |
| $C{$_} = s/(.)(.)/\e[${1};3${2}m/r =~ tr/ab/01/r; | |
| } | |
| $C{r} = "\e[m"; | |
| } | |
| sub usage { | |
| print STDERR << "EOF"; | |
| Usage: $program_name [options] | |
| Finds services and programs that need to be restarted because of stale | |
| file handles after a system update. | |
| Options: | |
| verbose Print details. | |
| EOF | |
| } | |
| ## returns list with removed duplicates | |
| sub uniq { | |
| my %seen; | |
| return grep { not $seen{$_}++ } @_; | |
| } | |
| ## see sub 'usage' for parameter descriptions | |
| my $verbose; | |
| for (@ARGV) { | |
| if ($_ and $_ eq substr("verbose", 0, length $_)) { | |
| $verbose = 1; | |
| } else { | |
| usage; | |
| exit 1; | |
| } | |
| } | |
| if ($< != 0) { | |
| if (not exec 'sudo', $0, @ARGV) { | |
| say STDERR "$program_name: Error! This program needs to run as root."; | |
| exit 1; | |
| } | |
| } | |
| #if ($< != 0) { | |
| #say STDERR "$program_name: Error! This program needs to run as root."; | |
| #exit 1; | |
| #} | |
| ## holds info for each process; keys are PIDs | |
| my %procs; | |
| ## use external 'lsof' to find processes that are using deleted files with | |
| ## file-names matching $whitelist and $blacklist | |
| $_ = qx'lsof +L1 -Fpcun 2> /dev/null; lsof -dDEL -Fpcun 2> /dev/null'; | |
| for (split m/^(?=p)/m) { | |
| my ($p, $c, $u) = (/^p(.*)/m, /^c(.*)/m, /^u(.*)/m); | |
| my @n; | |
| for (/^n(.*)/mg) { | |
| #push @n, $_ if /$whitelist/ and not /$blacklist/; | |
| push @n, $_ =~ s{.*/}{}r if /$whitelist/ and not /$blacklist/; | |
| } | |
| if (@n) { | |
| $procs{$p} = { | |
| PID => $p, | |
| UID => $u, | |
| NAME => \@n, | |
| COMMAND => $c | |
| }; | |
| } | |
| } | |
| ## prints details for a list of PIDs as recorded in %procs | |
| sub details { | |
| my $pids = shift; | |
| for (map { $procs{$_} } sort { $a <=> $b } @$pids) { | |
| printf "$C{a4}%6d $C{a3}%-15s$C{r} %s\n", | |
| $_->{PID}, | |
| $_->{COMMAND}, | |
| (join ", ", @{ $_->{NAME} }); | |
| } | |
| } | |
| if (%procs) { | |
| print << "END"; | |
| $C{b4}--------------------------------------------------------$C{r} | |
| $C{b7} Services and programs that might need to be restarted: $C{r} | |
| $C{b4}--------------------------------------------------------$C{r} | |
| END | |
| ## Use external 'ps' to find unit names for the processes. | |
| ## Create PID lists hashed by unit names with the regular user's processes | |
| ## split off into a separate hash. | |
| ## Assumes regular users are UID>=1000. | |
| my %units; my %units1000; | |
| $_ = join " ", keys %procs; | |
| $_ = qx{ps -o pid=,unit= -p $_ 2> /dev/null}; | |
| while (/^ *(\d+) *(.*)/mg) { | |
| my $ref = ($procs{$1}->{UID} < 1000) ? \%units : \%units1000; | |
| push @{ $ref->{$2} }, $1; | |
| } | |
| # my %units; my %units1000; | |
| # for (values %procs) { | |
| # my $ref = ($$_{UID} < 1000) ? \%units : \%units1000; | |
| # push @{ $$ref{$$_{UNIT}} }, $$_{PID}; | |
| # } | |
| ## print system units | |
| for (sort keys %units) { | |
| if ($verbose) { | |
| say "$C{b4}> $C{b1}$_$C{r}:"; | |
| details \@{ $units{$_} }; | |
| # say " ", join ", ", uniq map { @{$_} } map { $procs{$_}->{NAME} } @{$units{$_}}; | |
| } else { | |
| say "$C{b4}> $C{b3}$_$C{r}"; | |
| } | |
| } | |
| ## print user's units | |
| for (sort keys %units1000) { | |
| say "$C{b4}> $C{b2}$_$C{r}:"; | |
| # details \@{ (sort { $a cmp $b } @{ $units1000{$_} }) }; | |
| details \@{ $units1000{$_} }; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment