forked from stepb/urxvt-tabbedex
-
Notifications
You must be signed in to change notification settings - Fork 13
/
pgid-cd.pl
executable file
·146 lines (109 loc) · 3.34 KB
/
pgid-cd.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env perl
=head1 NAME
pgid-cd.pl - change directory to that of given process and run command
=head1 SYNOPSIS
pgid-cd.pl [ -v ] ( I<pgid> | -1 ) I<command> [ I<args> ... ]
=head1 DESCRIPTION
Tries to detect current working directory (or CWD) of specified process group
and runs specified command in that directory.
CWD detection is brittle and can lead to unexpected results. It is likely to
fail if all processes in given process group run as a user other than current
user (current user will simply lack permission to read CWD information).
Furthermore, if different processes in process group have different CWDs the
script will pick arbitrary one (it can access). And of course there's no hope
of it working via remote connection (e.g. SSH).
=head1 OPTIONS
=over
=item B<-v>
If changing directory fails, print all encountered errors. Otherwise silently
start the command. Note: if present, this *must* be the first argument.
=item I<pgid> or B<-1>
ID of the process group to detect CWD of. B<-1> disables CWD detection and the
script then simply executes the command.
=item I<command> [ I<args> ... ]
Command to execute.
=back
=head1 TABBEDEX
The script was designed to work with tabbedex urxvt plugin and in particular its
B<tab-arguments> configuration resource. For example, if this script is located
in B<~/.urxvt/tabbedex-pgid-cd> one can use the following configuration:
URxvt.tabbedex.tab-arguments: \
-e %~/.urxvt/tabbedex-pgid-cd %p %E
or if it's in B</usr/lib/urxvt/tabbedex-pgid-cd> then:
URxvt.tabbedex.tab-arguments: \
-e /usr/lib/urxvt/tabbedex-pgid-cd %p %E
Tabbedex will replace B<%p> sequence with an ID of a process in foreground of
current tab such that command in the new tab will inherit current working
directory from existing tab.
=head1 SEE ALSO
L<urxvt-tabbed(1)> and L<tabbedex-command-runner(1)>
=cut
use warnings;
use strict;
sub error {
print STDERR join(': ', $0, @_), "\n";
}
sub fatal {
error @_;
error 'use Ctrl+C to terminate this script';
sleep;
exit 1;
}
my $verbose = $ARGV[0] eq '-v';
if ($verbose) {
shift @ARGV;
}
my $pgid = shift @ARGV;
if ($pgid !~ /^(?:-1|\d+)$/ || !@ARGV) {
fatal 'usage: $0 [ -v ] ( <pgid> | -1 ) <command> [ <args> ... ]';
}
if ($pgid == -1) {
goto DONE;
} elsif (!-d '/proc') {
error('/proc missing, unable to determine CWD of other processes');
goto DONE;
}
my @errors;
sub try_pid {
my ($pid) = @_;
my $path = "/proc/$pid/cwd";
my $cwd = eval { readlink $path };
if ($@) {
push @errors, "$path: $@";
} elsif (!defined $cwd) {
push @errors, "$path: $!";
} elsif (!chdir $cwd) {
push @errors, "$cwd: $!";
} else {
goto DONE;
}
}
# If $pgid is still alive we can use its CWD
if (-d "/proc/$pgid") {
try_pid($pgid);
}
# If $pgid is no longer with us, we need to go through all running
# processes and filter the ones which are in process group $pgid.
if (opendir(my $dir, '/proc')) {
while (my $pid = readdir $dir) {
if ($pid =~ /^[0-9]+$/ && $pid != $pgid &&
open(my $fh, '<', "/proc/$pid/stat")) {
if ($pgid == (split ' ', scalar <$fh>)[4]) {
try_pid $pid;
}
}
}
} else {
push @errors, "/proc: $!";
}
if ($verbose) {
for (@errors) {
chomp;
error $_;
}
error("can't find " . (@errors ? 'CWD of' : 'process in') .
" group $pgid; won't change directory");
}
DONE:
{ exec { $ARGV[0] } @ARGV }
fatal $ARGV[0], "$!";