| 1 | #!/usr/bin/perl -w |
|---|
| 2 | # |
|---|
| 3 | # Munin plugin to monitor Tor routers |
|---|
| 4 | # |
|---|
| 5 | # Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>, based on a plugin by Ge van Geldorp <ge@gse.nl> |
|---|
| 6 | # |
|---|
| 7 | # Parameters understood: |
|---|
| 8 | # |
|---|
| 9 | # host - Change which host to graph (default localhost) |
|---|
| 10 | # port - Change which port to connect to (default 9051) |
|---|
| 11 | # password - Plain-text control channel password (see torrc |
|---|
| 12 | # HashedControlPassword parameter) |
|---|
| 13 | # cookiefile - Name of the file containing the control channel cookie |
|---|
| 14 | # (see torrc CookieAuthentication parameter) |
|---|
| 15 | # |
|---|
| 16 | # Using HashedControlPassword authentication has the problem that you must |
|---|
| 17 | # include the plain-text password in the munin config file. To have any |
|---|
| 18 | # effect, that file shouldn't be world-readable. |
|---|
| 19 | # If you're using CookieAuthentication, you should run this plugin as a user |
|---|
| 20 | # which has read access to the tor datafiles. Also note that bugs in versions |
|---|
| 21 | # upto and including 0.1.1.20 prevent CookieAuthentication from working. |
|---|
| 22 | # |
|---|
| 23 | # Usage: place in /etc/munin/node.d/ (or link it there using ln -s) |
|---|
| 24 | # |
|---|
| 25 | # Parameters understood: |
|---|
| 26 | # config (required) |
|---|
| 27 | # autoconf (optional - used by munin-config) |
|---|
| 28 | # |
|---|
| 29 | # |
|---|
| 30 | # Magic markers - optional - used by installation scripts and |
|---|
| 31 | # munin-config: |
|---|
| 32 | # |
|---|
| 33 | #%# family=contrib |
|---|
| 34 | #%# capabilities=autoconf |
|---|
| 35 | |
|---|
| 36 | use strict; |
|---|
| 37 | use IO::Socket::INET; |
|---|
| 38 | |
|---|
| 39 | # Config |
|---|
| 40 | our $address = $ENV{host} || "localhost"; # Default: localhost |
|---|
| 41 | our $port = $ENV{port} || 9051; # Default: 9051 |
|---|
| 42 | |
|---|
| 43 | # Don't edit below this line |
|---|
| 44 | |
|---|
| 45 | sub Authenticate |
|---|
| 46 | { |
|---|
| 47 | my ($socket) = @_; |
|---|
| 48 | my $authline = "AUTHENTICATE"; |
|---|
| 49 | if (defined($ENV{cookiefile})) { |
|---|
| 50 | if (open(COOKIE, "<$ENV{cookiefile}")) { |
|---|
| 51 | binmode COOKIE; |
|---|
| 52 | my $cookie; |
|---|
| 53 | $authline .= " "; |
|---|
| 54 | while (read(COOKIE, $cookie, 32)) { |
|---|
| 55 | foreach my $byte (unpack "C*", $cookie) { |
|---|
| 56 | $authline .= sprintf "%02x", $byte; |
|---|
| 57 | } |
|---|
| 58 | } |
|---|
| 59 | close COOKIE; |
|---|
| 60 | } |
|---|
| 61 | } elsif (defined($ENV{password})) { |
|---|
| 62 | $authline .= ' "' . $ENV{password} . '"'; |
|---|
| 63 | } |
|---|
| 64 | print $socket "$authline\r\n"; |
|---|
| 65 | my $replyline = <$socket>; |
|---|
| 66 | if (substr($replyline, 0, 1) != '2') { |
|---|
| 67 | $replyline =~ s/\s*$//; |
|---|
| 68 | return "Failed to authenticate: $replyline"; |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | return; |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | if ($ARGV[0] and $ARGV[0] eq "autoconf") { |
|---|
| 75 | # Try to connect to the daemon |
|---|
| 76 | my $socket = IO::Socket::INET->new("$address:$port") |
|---|
| 77 | or my $failed = 1; |
|---|
| 78 | |
|---|
| 79 | if ($failed) { |
|---|
| 80 | print "no (failed to connect to $address port $port)\n"; |
|---|
| 81 | exit 1; |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | my $msg = Authenticate($socket); |
|---|
| 85 | if (defined($msg)) { |
|---|
| 86 | print $socket "QUIT\r\n"; |
|---|
| 87 | close($socket); |
|---|
| 88 | print "no ($msg)\n"; |
|---|
| 89 | exit 1; |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | print $socket "QUIT\r\n"; |
|---|
| 93 | close($socket); |
|---|
| 94 | print "yes\n"; |
|---|
| 95 | exit 0; |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | if ($ARGV[0] and $ARGV[0] eq "config") { |
|---|
| 99 | print "graph_title Routers\n"; |
|---|
| 100 | print "graph_args -l 0\n"; |
|---|
| 101 | print "graph_vlabel routers\n"; |
|---|
| 102 | print "graph_category Tor\n"; |
|---|
| 103 | print "graph_info This graph shows the number of known Tor ORs.\n"; |
|---|
| 104 | |
|---|
| 105 | print "ors.label routers\n"; |
|---|
| 106 | print "ors.type GAUGE\n"; |
|---|
| 107 | print "ors.info The number of known Tor ORs (onion routers)\n"; |
|---|
| 108 | |
|---|
| 109 | exit 0; |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | my $socket = IO::Socket::INET->new("$address:$port") |
|---|
| 113 | or die("Couldn't connect to $address port $port: $!"); |
|---|
| 114 | |
|---|
| 115 | my $msg = Authenticate($socket); |
|---|
| 116 | if (defined($msg)) { |
|---|
| 117 | print $socket "QUIT\r\n"; |
|---|
| 118 | close($socket); |
|---|
| 119 | die "$msg\n"; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | print $socket "GETINFO ns/all\r\n"; |
|---|
| 123 | my $replyline = <$socket>; |
|---|
| 124 | if (substr($replyline, 0, 1) != '2') { |
|---|
| 125 | print $socket "QUIT\r\n"; |
|---|
| 126 | close($socket); |
|---|
| 127 | $replyline =~ s/\s*$//; |
|---|
| 128 | die "Failed to get orconn-status info: $replyline\n"; |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | my $count; |
|---|
| 132 | while (! (($replyline = <$socket>) =~ /^\.\s*$/)) { |
|---|
| 133 | my @reply = split(/\s+/, $replyline); |
|---|
| 134 | $count++ if $reply[0] eq 'r'; |
|---|
| 135 | } |
|---|
| 136 | $replyline = <$socket>; |
|---|
| 137 | if (substr($replyline, 0, 1) != '2') { |
|---|
| 138 | print $socket "QUIT\r\n"; |
|---|
| 139 | close($socket); |
|---|
| 140 | $replyline =~ s/\s*$//; |
|---|
| 141 | die "Failed to authenticate: $replyline\n"; |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | print $socket "QUIT\r\n"; |
|---|
| 145 | close($socket); |
|---|
| 146 | |
|---|
| 147 | print "ors.value $count\n"; |
|---|
| 148 | |
|---|
| 149 | exit 0; |
|---|
| 150 | |
|---|
| 151 | # vim:syntax=perl |
|---|