diff options
author | psyc://loupsycedyglgamf.onion/~lynX <ircs://psyced.org/youbroketheinternet> | 2016-08-10 15:28:56 +0000 |
---|---|---|
committer | psyc://loupsycedyglgamf.onion/~lynX <ircs://psyced.org/youbroketheinternet> | 2016-08-10 15:28:56 +0000 |
commit | a727e7f29a0b56121f825beb64074dad90a64752 (patch) | |
tree | 0e088f977c1ddf292af48d506f5a6372af57940d | |
parent | 25a19f76a99cc8e536fd19b533daea2c32213747 (diff) | |
download | perlpsyc-a727e7f29a0b56121f825beb64074dad90a64752.tar.gz perlpsyc-a727e7f29a0b56121f825beb64074dad90a64752.zip |
how come httpd2psyc was missing?
-rwxr-xr-x | bin/httpd2psyc | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/bin/httpd2psyc b/bin/httpd2psyc new file mode 100755 index 0000000..0875c14 --- /dev/null +++ b/bin/httpd2psyc | |||
@@ -0,0 +1,230 @@ | |||
1 | #!/usr/local/bin/perl -I/usr/depot/lib/perl5 | ||
2 | |||
3 | sub debug() { 0 } | ||
4 | sub chat() { 1 } | ||
5 | |||
6 | use IO::Socket; | ||
7 | use POSIX qw( mkfifo ); # optional, for -s option | ||
8 | use Pod::Usage qw( pod2usage ); | ||
9 | use Term::ANSIColor qw( :constants ); # optional but pervasive | ||
10 | use Net::PSYC qw( :event ); # needed for event loop | ||
11 | use Getopt::Std qw( getopt ); | ||
12 | getopt('timbls'); | ||
13 | |||
14 | my $bind = $opt_b || "psyc://$ENV{USER}\@127.0.0.1:4088/\$httpd"; | ||
15 | my $target = $opt_t || "psyc://127.0.0.1:4404/\@httpd"; | ||
16 | |||
17 | |||
18 | ## SANDWICHES ## | ||
19 | |||
20 | sub say { | ||
21 | print BOLD, GREEN, @_, RESET, "\n"; | ||
22 | } | ||
23 | |||
24 | sub reallyquit { exit; } | ||
25 | sub quit { | ||
26 | # error condition can be triggered several times | ||
27 | return if $quitting++; | ||
28 | tellpsyc(chat? "_request_leave_httpd": "_notice_stopping_httpd", "[_command] stopping on [_nick]."); | ||
29 | tellpsyc("_request_circuit_shutdown", "Seeyasoon."); | ||
30 | add(2, 't', \&reallyquit); | ||
31 | } | ||
32 | |||
33 | sub tellpsyc { | ||
34 | my ($mc, $text, $var, $value) = @_; | ||
35 | say "tellpsyc: ", $text if debug & 4; | ||
36 | return unless $target; | ||
37 | if (length $text > 1000) { | ||
38 | $mc = "_error_excessive_data$mc"; | ||
39 | $text = "[_amount_data] bytes of excess data ignored. Check console."; | ||
40 | $var = "_amount_data"; | ||
41 | $value = length $text; | ||
42 | } | ||
43 | sendmsg($target, $mc, $text, $value? { $var => $value } : \%pv); | ||
44 | } | ||
45 | |||
46 | sub fifoerror { | ||
47 | return if $quitting; | ||
48 | my $msg = "FIFO error: $_"; | ||
49 | say $msg; | ||
50 | tellpsyc('_notice_warning_httpd', $msg); | ||
51 | if ($opt_d) { | ||
52 | sleep 7; | ||
53 | goto REOPEN; | ||
54 | } | ||
55 | return &quit; | ||
56 | } | ||
57 | |||
58 | sub fifoparse { | ||
59 | $_ = <$fifo>; | ||
60 | chomp; chomp; | ||
61 | say "fifoparse($!): $_" if debug & 1; | ||
62 | fifoerror unless $_; | ||
63 | print $log $_, "\n" if $log; | ||
64 | # if httpd is giving us non-json input, we just send it as is | ||
65 | unless ( /^{ (.*)},$/ ) { | ||
66 | tellpsyc('_notice_access_httpd_raw', $_); | ||
67 | return; | ||
68 | } | ||
69 | $_ = $1; | ||
70 | @_ = split( /"([^"]*)":"([^"]*)", / ); | ||
71 | my %m = %pv; | ||
72 | while (@_) { | ||
73 | $e = shift; $k = shift; $v = shift; | ||
74 | print STDERR "JSON parse error: '$e' in { $_ }" if $e; | ||
75 | print "\t", GREEN, $k, RESET, "\t", $v, "\n" if debug & 8; | ||
76 | $m{$k} = $v; | ||
77 | } | ||
78 | $m{_network_traffic} = $m{_network_traffic_incoming} + $m{_network_traffic_outgoing}; | ||
79 | # only forward to psyced when not a transfer continuation | ||
80 | # if ( $m{_status_HTTP} eq 206 ) { | ||
81 | # print CYAN, " +", $m{_network_traffic}, " ", RESET; | ||
82 | # return; | ||
83 | # } | ||
84 | # print RED, "_", RESET; | ||
85 | print BOLD, $m{_status_HTTP}, " ", CYAN, $m{_network_address_IP}, "\t", | ||
86 | RESET, $m{_network_traffic}, "\t", BOLD, YELLOW, $m{_item_path}, | ||
87 | " ", GREEN, $m{_version_agent_HTTP}, RESET, "\n"; | ||
88 | # only forward messages about large files...... YMMV | ||
89 | return if $opt_s and $m{_network_traffic} < $opt_s || $m{_status_HTTP} eq 206; | ||
90 | sendmsg($target, '_notice_access_httpd', "[_network_address_IP] fetches [_item_path] ([_network_traffic] bytes) using [_version_agent_HTTP]", \%m); | ||
91 | } | ||
92 | |||
93 | sub keyparse { | ||
94 | $_ = <STDIN>; | ||
95 | say "keyparse: ", $_ if debug & 2; | ||
96 | return &quit if /^\s*q\s*$/i; | ||
97 | return system('/usr/bin/clear') if /^\.$/; | ||
98 | # should be _converse | ||
99 | return tellpsyc("_message_public", $1) if m!^\s*/\s+(.*\S.*)$!; | ||
100 | } | ||
101 | |||
102 | sub msg { | ||
103 | my ($source, $mc, $data, $vars) = @_; | ||
104 | say "psyc: $mc from $source" if debug & 4; | ||
105 | if ($mc =~ /^_(message|converse)/) { | ||
106 | # message echo will pass through here. legitimate users of | ||
107 | # your PSYC server can send a message back to the localhost | ||
108 | # process, so you may see an answer from somebody. | ||
109 | if (exists $vars->{_nick}) { | ||
110 | $_ = "<". $vars->{_nick} ."> ". YELLOW . $data; | ||
111 | } else { | ||
112 | $_ = "(". $source .") ". YELLOW . $data; | ||
113 | } | ||
114 | } elsif (chat and $mc =~ /^_notice.*_httpd/) { | ||
115 | return; # do not display our own notices | ||
116 | } else { | ||
117 | $_ = psyctext($data, $vars); | ||
118 | } | ||
119 | print BOLD, MAGENTA, $_, RESET, "\n"; | ||
120 | } | ||
121 | |||
122 | sub idlebilly { | ||
123 | tellpsyc('_notice_warning_idle_httpd', "[_command] has detected idling [_nick] httpd."); | ||
124 | } | ||
125 | |||
126 | sub mkdirhier { | ||
127 | my $d = shift; | ||
128 | if (-d $d) { | ||
129 | # print STDERR BLUE, "(directory $d already exists)\n", RESET; | ||
130 | } else { | ||
131 | if (system('/bin/mkdir', '-p', $d)) { | ||
132 | undef $!; | ||
133 | die "Could not make directory hierarchy: $!" | ||
134 | if system('mkdirhier', $d); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | ## MAIN ### | ||
140 | |||
141 | say "httpd2psyc - a real-time httpd log analyzer with PSYC notification"; | ||
142 | $nick = shift || 'default'; | ||
143 | if ($opt_h) { | ||
144 | pod2usage; | ||
145 | } | ||
146 | |||
147 | $pv{_nick} = $nick; | ||
148 | $pv{_command} = $0; | ||
149 | bind_uniform( $bind ); | ||
150 | register_uniform(); | ||
151 | say "Listening for PSYC controls on $bind"; | ||
152 | say "Sending PSYC notifications to $target"; | ||
153 | |||
154 | $|=1; | ||
155 | |||
156 | $fifopath = $opt_m || "/var/run/httpd.fifo"; | ||
157 | if (not -p $fifopath) { | ||
158 | $fifopath =~ m#(.*)/[^/]+$#; | ||
159 | mkdirhier( $1 ) if $1; | ||
160 | undef $!; | ||
161 | die "Could not mkfifo $fifopath $!" unless mkfifo $fifopath, 0600; | ||
162 | # system('chgrp', 'www', $fifopath); | ||
163 | # chmod 0660, $fifopath; | ||
164 | } | ||
165 | say "Monitoring $fifopath for HTTP events"; | ||
166 | REOPEN: | ||
167 | if (open($fifo, $fifopath)) { | ||
168 | add($fifo, 'r', \&fifoparse); | ||
169 | add($fifo, 'e', \&fifoerror); | ||
170 | } else { | ||
171 | die "Cannot read from $fifopath: $!"; | ||
172 | } | ||
173 | die "Cannot write to $opt_l: $!" | ||
174 | if $opt_l and not open($log, '>>', $opt_l); | ||
175 | add(\*STDIN, 'r', \&keyparse); | ||
176 | add($opt_i, 'i', \&idlebilly) if $opt_i; | ||
177 | tellpsyc(chat? "_request_enter_httpd": "_notice_starting_httpd", "[_command] starting on [_nick]."); | ||
178 | start_loop(); | ||
179 | exit; | ||
180 | |||
181 | __END__ | ||
182 | |||
183 | =pod | ||
184 | |||
185 | =head1 NAME | ||
186 | |||
187 | httpd2psyc - a real-time httpd log analyzer with PSYC notification | ||
188 | |||
189 | =head1 SYNOPSIS | ||
190 | |||
191 | httpd2psyc [<options>] [<nickname>] | ||
192 | |||
193 | Options: | ||
194 | -m <path> (m)onitor a FIFO for interesting HTTP events | ||
195 | -t <uniform> PSYC (t)arget address to send notifications to | ||
196 | -d (D)aemon: reopen FIFO whenever httpd closes it (on restart) | ||
197 | -s <bytes> Threshold of traffic (s)ize before forwarding is triggered | ||
198 | -b <uniform> PSYC address to (b)ind to | ||
199 | -l <logfile> Keep a transaction (l)og - BEWARE OF GDPR! | ||
200 | -i <seconds> Send a PSYC warning if httpd has been (i)dle too long | ||
201 | |||
202 | =head1 DESCRIPTION | ||
203 | |||
204 | HTTP logfile to PSYC gateway. Works by sending httpd logfile output | ||
205 | into a FIFO and having this script pick it up. For optimal rendering | ||
206 | you should configure your httpd to output JSON-PSYC format, using | ||
207 | something like this: | ||
208 | |||
209 | LogFormat "{ \"_time_JSON\":\"%{%Y-%m-%d}tT%{%T}t.%{msec_frac}tZ\", \"_system_process\":\"%D\", \"_item_size\":\"%B\", \"_item_path\":\"%f\", \"_network_address_IP\":\"%a\", \"_site_HTTP\":\"%V\", \"_request_HTTP\":\"%U\", \"_query_HTTP\":\"%q\", \"_method_HTTP\":\"%m\", \"_status_HTTP\":\"%>s\", \"_version_agent_HTTP\":\"%{User-agent}i\", \"_uniform_referrer\":\"%{Referer}i\", \"_network_traffic_incoming\":\"%I\", \"_network_traffic_outgoing\":\"%O\", }," jsonpsyc | ||
210 | CustomLog /var/run/httpd.fifo jsonpsyc | ||
211 | |||
212 | You should run 'httpd2psyc -m /var/run/httpd.fifo' first so that | ||
213 | it takes care of mkfifo. | ||
214 | |||
215 | =head1 PRIVACY | ||
216 | |||
217 | Remember that you are not supposed to store any IP numbers or other | ||
218 | personal data if you intend to be GDPR compliant. This is just for | ||
219 | realtime monitoring. | ||
220 | |||
221 | =head1 AUTHORS | ||
222 | |||
223 | carlo von lynX. | ||
224 | |||
225 | =head1 COPYRIGHT | ||
226 | |||
227 | This program is free software, published under the Affero GNU Public | ||
228 | License. A disclaimer isn't necessary in my country, nor do I need | ||
229 | to mention the current year to assert a copyright. | ||
230 | |||