1#!/usr/local/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# up to 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 36use strict; 37use IO::Socket::INET; 38 39# Config 40our $address = $ENV{host} || "localhost"; # Default: localhost 41our $port = $ENV{port} || 9051; # Default: 9051 42 43# Don't edit below this line 44 45sub 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 74if ($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 0; 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 0; 90 } 91 92 print $socket "QUIT\r\n"; 93 close($socket); 94 print "yes\n"; 95 exit 0; 96} 97 98if ($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 network\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 112my $socket = IO::Socket::INET->new("$address:$port") 113 or die("Couldn't connect to $address port $port: $!"); 114 115my $msg = Authenticate($socket); 116if (defined($msg)) { 117 print $socket "QUIT\r\n"; 118 close($socket); 119 die "$msg\n"; 120} 121 122print $socket "GETINFO ns/all\r\n"; 123my $replyline = <$socket>; 124if (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 131my $count; 132while (! (($replyline = <$socket>) =~ /^\.\s*$/)) { 133 my @reply = split(/\s+/, $replyline); 134 $count++ if $reply[0] eq 'r'; 135} 136$replyline = <$socket>; 137if (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 144print $socket "QUIT\r\n"; 145close($socket); 146 147print "ors.value $count\n"; 148 149exit 0; 150 151# vim:syntax=perl 152