#!/usr/bin/perl # # collectd - contrib/cussh.pl # Copyright (C) 2007-2009 Sebastian Harl # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; only version 2 of the License is applicable. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # Author: # Sebastian Harl # =head1 NAME cussh - collectd UNIX socket shell =head1 SYNOPSIS B [IpathE>] =head1 DESCRIPTION B's unixsock plugin allows external programs to access the values it has collected or received and to submit own values. This is a little interactive frontend for this plugin. =head1 OPTIONS =over 4 =item IpathE> The path to the UNIX socket provided by collectd's unixsock plugin. (Default: F) =back =cut use strict; use warnings; use Collectd::Unixsock(); { # main my $path = $ARGV[0] || "/var/run/collectd-unixsock"; my $sock = Collectd::Unixsock->new($path); my $cmds = { HELP => \&cmd_help, PUTVAL => \&putval, GETVAL => \&getval, GETTHRESHOLD => \&getthreshold, FLUSH => \&flush, LISTVAL => \&listval, PUTNOTIF => \&putnotif, }; if (! $sock) { print STDERR "Unable to connect to $path!\n"; exit 1; } print "cussh version 0.2, Copyright (C) 2007-2008 Sebastian Harl\n" . "cussh comes with ABSOLUTELY NO WARRANTY. This is free software,\n" . "and you are welcome to redistribute it under certain conditions.\n" . "See the GNU General Public License 2 for more details.\n\n"; while (1) { print "cussh> "; my $line = ; last if (! $line); chomp $line; last if ($line =~ m/^quit$/i); my ($cmd) = $line =~ m/^(\w+)\s*/; $line = $'; next if (! $cmd); $cmd = uc $cmd; my $f = undef; if (defined $cmds->{$cmd}) { $f = $cmds->{$cmd}; } else { print STDERR "ERROR: Unknown command $cmd!\n"; next; } if (! $f->($sock, $line)) { print STDERR "ERROR: Command failed!\n"; next; } } $sock->destroy(); exit 0; } sub tokenize { my $line = shift || return; my $line_ptr = $line; my @line = (); my $token_pattern = qr/[^"\s]+|"[^"]+"/; while (my ($token) = $line_ptr =~ m/^($token_pattern)\s+/) { $line_ptr = $'; push @line, $token; } if ($line_ptr =~ m/^$token_pattern$/) { push @line, $line_ptr; } else { my ($token) = split m/ /, $line_ptr, 1; print STDERR "Failed to parse line: $line\n"; print STDERR "Parse error near token \"$token\".\n"; return; } foreach my $l (@line) { if ($l =~ m/^"(.*)"$/) { $l = $1; } } return @line; } sub getid { my $string = shift || return; my ($h, $p, $pi, $t, $ti) = $string =~ m#^([^/]+)/([^/-]+)(?:-([^/]+))?/([^/-]+)(?:-([^/]+))?\s*#; $string = $'; return if ((! $h) || (! $p) || (! $t)); my %id = (); ($id{'host'}, $id{'plugin'}, $id{'type'}) = ($h, $p, $t); $id{'plugin_instance'} = $pi if defined ($pi); $id{'type_instance'} = $ti if defined ($ti); return \%id; } sub putid { my $ident = shift || return; my $string; $string = $ident->{'host'} . "/" . $ident->{'plugin'}; if (defined $ident->{'plugin_instance'}) { $string .= "-" . $ident->{'plugin_instance'}; } $string .= "/" . $ident->{'type'}; if (defined $ident->{'type_instance'}) { $string .= "-" . $ident->{'type_instance'}; } return $string; } =head1 COMMANDS =over 4 =item B =cut sub cmd_help { my $sock = shift; my $line = shift || ''; my @line = tokenize($line); my $cmd = shift (@line); my %text = ( help => < < [ ...] Submits a value to the daemon. HELP getval => < Retrieves the current value or values from the daemon. HELP flush => <] [timeout=] [identifier=] [...] Sends a FLUSH command to the daemon. HELP listval => < < [...] message= Sends a notifications message to the daemon. HELP ); if (!$cmd) { $cmd = 'help'; } if (!exists ($text{$cmd})) { print STDOUT "Unknown command: " . uc ($cmd) . "\n\n"; $cmd = 'help'; } print STDOUT $text{$cmd}; return 1; } # cmd_help =item B I I =cut sub putval { my $sock = shift || return; my $line = shift || return; my @line = tokenize($line); my $id; my $ret; if (! @line) { return; } if (scalar(@line) < 2) { print STDERR "Synopsis: PUTVAL [ ...]" . $/; return; } $id = getid($line[0]); if (! $id) { print STDERR "Invalid id \"$line[0]\"." . $/; return; } my ($time, @values) = split m/:/, $line; $ret = $sock->putval(%$id, time => $time, values => \@values); if (! $ret) { print STDERR "socket error: " . $sock->{'error'} . $/; } return $ret; } =item B I =cut sub getval { my $sock = shift || return; my $line = shift || return; my @line = tokenize($line); my $id; my $vals; if (! @line) { return; } if (scalar(@line) < 1) { print STDERR "Synopsis: GETVAL " . $/; return; } $id = getid($line[0]); if (! $id) { print STDERR "Invalid id \"$line[0]\"." . $/; return; } $vals = $sock->getval(%$id); if (! $vals) { print STDERR "socket error: " . $sock->{'error'} . $/; return; } foreach my $key (keys %$vals) { print "\t$key: $vals->{$key}\n"; } return 1; } =item B I =cut sub getthreshold { my $sock = shift || return; my $line = shift || return; my @line = tokenize($line); my $id; my $vals; if (! @line) { return; } if (scalar(@line) < 1) { print STDERR "Synopsis: GETTHRESHOLD " . $/; return; } $id = getid($line[0]); if (! $id) { print STDERR "Invalid id \"$line[0]\"." . $/; return; } $vals = $sock->getthreshold(%$id); if (! $vals) { print STDERR "socket error: " . $sock->{'error'} . $/; return; } foreach my $key (keys %$vals) { print "\t$key: $vals->{$key}\n"; } return 1; } =item B [B=I<$timeout>] [B=I<$plugin>[ ...]] =cut sub flush { my $sock = shift || return; my $line = shift; my @line = tokenize($line); my $res; if (! $line) { $res = $sock->flush(); } else { my %args = (); foreach my $i (@line) { my ($option, $value) = $i =~ m/^([^=]+)=(.+)$/; next if (! ($option && $value)); if ($option eq "plugin") { push @{$args{"plugins"}}, $value; } elsif ($option eq "timeout") { $args{"timeout"} = $value; } elsif ($option eq "identifier") { my $id = getid ($value); if (!$id) { print STDERR "Not a valid identifier: \"$value\"\n"; next; } push @{$args{"identifier"}}, $id; } else { print STDERR "Invalid option \"$option\".\n"; return; } } $res = $sock->flush(%args); } if (! $res) { print STDERR "socket error: " . $sock->{'error'} . $/; } return $res; } =item B =cut sub listval { my $sock = shift || return; my $line = shift; my @res; if ($line ne "") { print STDERR "Synopsis: LISTVAL" . $/; return; } @res = $sock->listval(); if (! @res) { print STDERR "socket error: " . $sock->{'error'} . $/; return; } foreach my $ident (@res) { print $ident->{'time'} . " " . putid($ident) . $/; } return 1; } =item B [[B=I<$severity>] [B=I<$message>] [ ...]] =cut sub putnotif { my $sock = shift || return; my $line = shift || return; my @line = tokenize($line); my $ret; my (%values) = (); foreach my $i (@line) { my ($key, $val) = split m/=/, $i, 2; if ($key && $val) { $values{$key} = $val; } else { $values{'message'} = defined($values{'message'}) ? ($values{'message'} . ' ' . $key) : $key; } } $values{'time'} ||= time(); $ret = $sock->putnotif(%values); if (! $ret) { print STDERR "socket error: " . $sock->{'error'} . $/; } return $ret; } =back These commands follow the exact same syntax as described in L. =head1 SEE ALSO L, L =head1 AUTHOR Written by Sebastian Harl Esh@tokkee.orgE. B has been written by Florian Forster and others. =head1 COPYRIGHT Copyright (C) 2007 Sebastian Harl. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; only version 2 of the License is applicable. =cut # vim: set sw=4 ts=4 tw=78 noexpandtab :