line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
################################################################# |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Hook::Filter - A runtime filtering layer on top of subroutine calls |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# $Id: Filter.pm,v 1.12 2008/08/26 08:13:30 erwan_lemonnier Exp $ |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# 051105 erwan Created |
8
|
|
|
|
|
|
|
# 060301 erwan Recreated |
9
|
|
|
|
|
|
|
# 070516 erwan Updated POD and license, added flush_rules and add_rule |
10
|
|
|
|
|
|
|
# 070522 erwan More POD + don't use rule file unless 'rules' specified in import |
11
|
|
|
|
|
|
|
# 070523 erwan Can use 'rules' multiple time if same rule file specified |
12
|
|
|
|
|
|
|
# 070523 erwan POD updates |
13
|
|
|
|
|
|
|
# 070524 erwan Import parameter 'hook' is now mandatory |
14
|
|
|
|
|
|
|
# |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
package Hook::Filter; |
17
|
|
|
|
|
|
|
|
18
|
6
|
|
|
6
|
|
306694
|
use 5.006; |
|
6
|
|
|
|
|
26
|
|
|
6
|
|
|
|
|
256
|
|
19
|
6
|
|
|
6
|
|
38
|
use strict; |
|
6
|
|
|
|
|
25
|
|
|
6
|
|
|
|
|
213
|
|
20
|
6
|
|
|
6
|
|
31
|
use warnings; |
|
6
|
|
|
|
|
15
|
|
|
6
|
|
|
|
|
276
|
|
21
|
6
|
|
|
6
|
|
171
|
use Carp qw(confess croak); |
|
6
|
|
|
|
|
17
|
|
|
6
|
|
|
|
|
501
|
|
22
|
6
|
|
|
6
|
|
41
|
use File::Spec; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
171
|
|
23
|
6
|
|
|
6
|
|
2774
|
use Hook::Filter::Rule; |
|
6
|
|
|
|
|
21
|
|
|
6
|
|
|
|
|
212
|
|
24
|
6
|
|
|
6
|
|
44
|
use Hook::Filter::RulePool qw(get_rule_pool); |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
374
|
|
25
|
6
|
|
|
6
|
|
39
|
use Hook::Filter::Hooker qw(filter_sub); |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
529
|
|
26
|
6
|
|
|
6
|
|
37
|
use base qw(Exporter); |
|
6
|
|
|
|
|
16
|
|
|
6
|
|
|
|
|
550
|
|
27
|
6
|
|
|
6
|
|
38
|
use Data::Dumper; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
8068
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
our @EXPORT = qw(); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $VERSION = '0.10'; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
#---------------------------------------------------------------- |
34
|
|
|
|
|
|
|
# |
35
|
|
|
|
|
|
|
# Global vars |
36
|
|
|
|
|
|
|
# |
37
|
|
|
|
|
|
|
#---------------------------------------------------------------- |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# the rule file actually used by Hook::Filter, and as declared with parameter 'rules' |
40
|
|
|
|
|
|
|
my $RULES_FILE; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# list of subs to hijack |
43
|
|
|
|
|
|
|
my %HOOK_SUBS; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub _queue_sub { |
46
|
19
|
|
|
19
|
|
29
|
my ($pkg,$name) = @_; |
47
|
19
|
100
|
|
|
|
100
|
($name =~ /::/) ? $HOOK_SUBS{$name}=1 : $HOOK_SUBS{$pkg."::".$name}=1; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
#---------------------------------------------------------------- |
51
|
|
|
|
|
|
|
# |
52
|
|
|
|
|
|
|
# import - verify import parameters, filter the subs and load the rule file |
53
|
|
|
|
|
|
|
# |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub import { |
56
|
17
|
|
|
17
|
|
18708
|
my($class,%args) = @_; |
57
|
17
|
|
|
|
|
43
|
my $pkg = caller(0); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# |
60
|
|
|
|
|
|
|
# check parameter 'rules', indicating path to the rule file |
61
|
|
|
|
|
|
|
# |
62
|
|
|
|
|
|
|
|
63
|
17
|
100
|
|
|
|
72
|
if (exists $args{rules}) { |
64
|
|
|
|
|
|
|
|
65
|
7
|
100
|
|
|
|
279
|
croak "import parameter 'rules' for Hook::Filter should be a string, but was undef." |
66
|
|
|
|
|
|
|
if (!defined $args{rules}); |
67
|
|
|
|
|
|
|
|
68
|
6
|
100
|
|
|
|
27
|
croak "import parameter 'rules' for Hook::Filter should be a string, but was [".Dumper($args{rules})."]." |
69
|
|
|
|
|
|
|
if (ref \$args{rules} ne 'SCALAR'); |
70
|
|
|
|
|
|
|
|
71
|
5
|
100
|
100
|
|
|
261
|
croak "you tried to specify 2 different Hook::Filter rule file: [$RULES_FILE] and [".$args{rules}."]. you may have only 1 rule file." |
72
|
|
|
|
|
|
|
if (defined $RULES_FILE && $RULES_FILE ne $args{rules}); |
73
|
|
|
|
|
|
|
|
74
|
4
|
|
|
|
|
8
|
$RULES_FILE = $args{rules}; |
75
|
4
|
|
|
|
|
9
|
delete $args{rules}; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# |
78
|
|
|
|
|
|
|
# load the rule file, if any |
79
|
|
|
|
|
|
|
# |
80
|
|
|
|
|
|
|
|
81
|
4
|
|
|
|
|
13
|
my $pool = get_rule_pool(); |
82
|
|
|
|
|
|
|
|
83
|
4
|
100
|
|
|
|
86
|
if (-f $RULES_FILE) { |
84
|
|
|
|
|
|
|
# TODO: support runtime monitoring of rules file and update of rules upon changes in file |
85
|
|
|
|
|
|
|
|
86
|
1
|
50
|
|
|
|
46
|
open(IN,"$RULES_FILE") |
87
|
|
|
|
|
|
|
or confess "failed to open Hook::Filter rules file [$RULES_FILE]: $!"; |
88
|
1
|
|
|
|
|
21
|
while (my $line = ) { |
89
|
9
|
|
|
|
|
16
|
chomp $line; |
90
|
9
|
100
|
|
|
|
39
|
next if ($line =~ /^\s*\#/); |
91
|
6
|
100
|
|
|
|
23
|
next if ($line =~ /^\s*$/); |
92
|
|
|
|
|
|
|
|
93
|
5
|
|
|
|
|
18
|
my $rule = new Hook::Filter::Rule($line); |
94
|
5
|
|
|
|
|
15
|
$rule->source($RULES_FILE); |
95
|
5
|
|
|
|
|
25
|
$pool->add_rule($rule); |
96
|
|
|
|
|
|
|
} |
97
|
1
|
|
|
|
|
11
|
close(IN); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# |
102
|
|
|
|
|
|
|
# check parameter 'hook', indicating which subroutines to filter |
103
|
|
|
|
|
|
|
# |
104
|
|
|
|
|
|
|
|
105
|
14
|
100
|
|
|
|
354
|
croak "you must call Hook::Filter with the import parameter 'hook' set to something" |
106
|
|
|
|
|
|
|
if (!exists $args{hook}); |
107
|
|
|
|
|
|
|
|
108
|
12
|
100
|
|
|
|
203
|
croak "Invalid parameter: 'hook' should be a string or an array of strings, but was undef." |
109
|
|
|
|
|
|
|
if (!defined $args{hook}); |
110
|
|
|
|
|
|
|
|
111
|
11
|
100
|
|
|
|
160
|
if (ref $args{hook} eq 'ARRAY') { |
|
|
100
|
|
|
|
|
|
112
|
5
|
|
|
|
|
12
|
foreach my $name (@{$args{hook}}) { |
|
5
|
|
|
|
|
19
|
|
113
|
15
|
100
|
|
|
|
294
|
if (ref \$name ne 'SCALAR') { |
114
|
1
|
|
|
|
|
5
|
croak "Invalid parameter: 'hook' for Hook::Filter should be a string or an array of strings, but was [".Dumper($args{hook})."]."; |
115
|
|
|
|
|
|
|
} |
116
|
14
|
|
|
|
|
27
|
_queue_sub($pkg,$name); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} elsif (ref \$args{hook} eq 'SCALAR') { |
119
|
5
|
|
|
|
|
17
|
_queue_sub($pkg,$args{hook}); |
120
|
|
|
|
|
|
|
} else { |
121
|
1
|
|
|
|
|
9
|
croak "Invalid parameter: 'hook' for Hook::Filter should be a string or an array of strings, but was [".Dumper($args{hook})."]."; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
9
|
|
|
|
|
24
|
delete $args{hook}; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# propagate super class's import |
127
|
9
|
|
|
|
|
915
|
$class->export_to_level(1,undef,()); |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# |
131
|
|
|
|
|
|
|
# when all is compiled, do filter all the subs |
132
|
|
|
|
|
|
|
# |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub _filter_subs { |
135
|
6
|
|
|
6
|
|
132
|
map { filter_sub($_) } keys %HOOK_SUBS; |
|
19
|
|
|
|
|
343
|
|
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# this init block won't be executed if Hook::Filter is used from an eval/require |
139
|
|
|
|
|
|
|
INIT { |
140
|
|
|
|
|
|
|
# add a filtering closure around each sub |
141
|
5
|
|
|
5
|
|
24
|
_filter_subs; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
1; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
__END__ |