#!/usr/bin/perl -w # # Munin plugin to monitor Tor routers # # Author: Ævar Arnfjörð Bjarmason , based on a plugin by Ge van Geldorp # # Parameters understood: # # host - Change which host to graph (default localhost) # port - Change which port to connect to (default 9051) # password - Plain-text control channel password (see torrc # HashedControlPassword parameter) # cookiefile - Name of the file containing the control channel cookie # (see torrc CookieAuthentication parameter) # # Using HashedControlPassword authentication has the problem that you must # include the plain-text password in the munin config file. To have any # effect, that file shouldn't be world-readable. # If you're using CookieAuthentication, you should run this plugin as a user # which has read access to the tor datafiles. Also note that bugs in versions # up to and including 0.1.1.20 prevent CookieAuthentication from working. # # Usage: place in /etc/munin/node.d/ (or link it there using ln -s) # # Parameters understood: # config (required) # autoconf (optional - used by munin-config) # # # Magic markers - optional - used by installation scripts and # munin-config: # #%# family=contrib #%# capabilities=autoconf use strict; use IO::Socket::INET; # Config our $address = $ENV{host} || "localhost"; # Default: localhost our $port = $ENV{port} || 9051; # Default: 9051 # Don't edit below this line sub Authenticate { my ($socket) = @_; my $authline = "AUTHENTICATE"; if (defined($ENV{cookiefile})) { if (open(COOKIE, "<$ENV{cookiefile}")) { binmode COOKIE; my $cookie; $authline .= " "; while (read(COOKIE, $cookie, 32)) { foreach my $byte (unpack "C*", $cookie) { $authline .= sprintf "%02x", $byte; } } close COOKIE; } } elsif (defined($ENV{password})) { $authline .= ' "' . $ENV{password} . '"'; } print $socket "$authline\r\n"; my $replyline = <$socket>; if (substr($replyline, 0, 1) != '2') { $replyline =~ s/\s*$//; return "Failed to authenticate: $replyline"; } return; } if ($ARGV[0] and $ARGV[0] eq "autoconf") { # Try to connect to the daemon my $socket = IO::Socket::INET->new("$address:$port") or my $failed = 1; if ($failed) { print "no (failed to connect to $address port $port)\n"; exit 1; } my $msg = Authenticate($socket); if (defined($msg)) { print $socket "QUIT\r\n"; close($socket); print "no ($msg)\n"; exit 1; } print $socket "QUIT\r\n"; close($socket); print "yes\n"; exit 0; } if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Routers\n"; print "graph_args -l 0\n"; print "graph_vlabel routers\n"; print "graph_category Tor\n"; print "graph_info This graph shows the number of known Tor ORs.\n"; print "ors.label routers\n"; print "ors.type GAUGE\n"; print "ors.info The number of known Tor ORs (onion routers)\n"; exit 0; } my $socket = IO::Socket::INET->new("$address:$port") or die("Couldn't connect to $address port $port: $!"); my $msg = Authenticate($socket); if (defined($msg)) { print $socket "QUIT\r\n"; close($socket); die "$msg\n"; } print $socket "GETINFO ns/all\r\n"; my $replyline = <$socket>; if (substr($replyline, 0, 1) != '2') { print $socket "QUIT\r\n"; close($socket); $replyline =~ s/\s*$//; die "Failed to get orconn-status info: $replyline\n"; } my $count; while (! (($replyline = <$socket>) =~ /^\.\s*$/)) { my @reply = split(/\s+/, $replyline); $count++ if $reply[0] eq 'r'; } $replyline = <$socket>; if (substr($replyline, 0, 1) != '2') { print $socket "QUIT\r\n"; close($socket); $replyline =~ s/\s*$//; die "Failed to authenticate: $replyline\n"; } print $socket "QUIT\r\n"; close($socket); print "ors.value $count\n"; exit 0; # vim:syntax=perl