contrib/cussh.pl: Improved error reporting.
[collectd.git] / contrib / cussh.pl
1 #!/usr/bin/perl
2 #
3 # collectd - contrib/cussh.pl
4 # Copyright (C) 2007-2008  Sebastian Harl
5 #
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the
8 # Free Software Foundation; only version 2 of the License is applicable.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18 #
19 # Author:
20 #   Sebastian Harl <sh at tokkee.org>
21 #
22
23 =head1 NAME
24
25 cussh - collectd UNIX socket shell
26
27 =head1 SYNOPSIS
28
29 B<cussh> [I<E<lt>pathE<gt>>]
30
31 =head1 DESCRIPTION
32
33 B<collectd>'s unixsock plugin allows external programs to access the values it
34 has collected or received and to submit own values. This is a little
35 interactive frontend for this plugin.
36
37 =head1 OPTIONS
38
39 =over 4
40
41 =item I<E<lt>pathE<gt>>
42
43 The path to the UNIX socket provided by collectd's unixsock plugin. (Default:
44 F</var/run/collectd-unixsock>)
45
46 =back
47
48 =cut
49
50 use strict;
51 use warnings;
52
53 use Collectd::Unixsock();
54
55 { # main
56         my $path = $ARGV[0] || "/var/run/collectd-unixsock";
57         my $sock = Collectd::Unixsock->new($path);
58
59         my $cmds = {
60                 HELP    => \&cmd_help,
61                 PUTVAL  => \&putval,
62                 GETVAL  => \&getval,
63                 FLUSH   => \&flush,
64                 LISTVAL => \&listval,
65                 PUTNOTIF => \&putnotif,
66         };
67
68         if (! $sock) {
69                 print STDERR "Unable to connect to $path!\n";
70                 exit 1;
71         }
72
73         print "cussh version 0.2, Copyright (C) 2007-2008 Sebastian Harl\n"
74                 . "cussh comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
75                 . "and you are welcome to redistribute it under certain conditions.\n"
76                 . "See the GNU General Public License 2 for more details.\n\n";
77
78         while (1) {
79                 print "cussh> ";
80                 my $line = <STDIN>;
81
82                 last if (! $line);
83
84                 chomp $line;
85
86                 last if ($line =~ m/^quit$/i);
87
88                 my ($cmd) = $line =~ m/^(\w+)\s*/;
89                 $line = $';
90
91                 next if (! $cmd);
92                 $cmd = uc $cmd;
93
94                 my $f = undef;
95                 if (defined $cmds->{$cmd}) {
96                         $f = $cmds->{$cmd};
97                 }
98                 else {
99                         print STDERR "ERROR: Unknown command $cmd!\n";
100                         next;
101                 }
102
103                 if (! $f->($sock, $line)) {
104                         print STDERR "ERROR: Command failed!\n";
105                         next;
106                 }
107         }
108
109         $sock->destroy();
110         exit 0;
111 }
112
113 sub getid {
114         my $string = shift || return;
115
116         print $$string . $/;
117         my ($h, $p, $pi, $t, $ti) =
118                 $$string =~ m#^([^/]+)/([^/-]+)(?:-([^/]+))?/([^/-]+)(?:-([^/]+))?\s*#;
119         $$string = $';
120
121         return if ((! $h) || (! $p) || (! $t));
122
123         my %id = ();
124
125         ($id{'host'}, $id{'plugin'}, $id{'type'}) = ($h, $p, $t);
126
127         $id{'plugin_instance'} = $pi if defined ($pi);
128         $id{'type_instance'} = $ti if defined ($ti);
129         return \%id;
130 }
131
132 sub putid {
133         my $ident = shift || return;
134
135         my $string;
136
137         $string = $ident->{'host'} . "/" . $ident->{'plugin'};
138
139         if (defined $ident->{'plugin_instance'}) {
140                 $string .= "-" . $ident->{'plugin_instance'};
141         }
142
143         $string .= "/" . $ident->{'type'};
144
145         if (defined $ident->{'type_instance'}) {
146                 $string .= "-" . $ident->{'type_instance'};
147         }
148         return $string;
149 }
150
151 =head1 COMMANDS
152
153 =over 4
154
155 =item B<HELP>
156
157 =cut
158
159 sub cmd_help {
160         print <<HELP;
161 Available commands:
162   HELP
163   PUTVAL
164   GETVAL
165   FLUSH
166   LISTVAL
167   PUTNOTIF
168
169 See the embedded Perldoc documentation for details. To do that, run:
170   perldoc $0
171 HELP
172         return 1;
173 } # cmd_help
174
175 =item B<GETVAL> I<Identifier>
176
177 =cut
178
179 sub putval {
180         my $sock = shift || return;
181         my $line = shift || return;
182
183         my $id = getid(\$line);
184
185         my $ret;
186
187         if (! $id) {
188                 print STDERR "Invalid id \"$line\"." . $/;
189                 return;
190         }
191
192         my ($time, @values) = split m/:/, $line;
193         $ret = $sock->putval(%$id, time => $time, values => \@values);
194
195         if (! $ret) {
196                 print STDERR "socket error: " . $sock->{'error'} . $/;
197         }
198         return $ret;
199 }
200
201 =item B<PUTVAL> I<Identifier> I<Valuelist>
202
203 =cut
204
205 sub getval {
206         my $sock = shift || return;
207         my $line = shift || return;
208
209         my $id = getid(\$line);
210
211         if (! $id) {
212                 print STDERR "Invalid id \"$line\"." . $/;
213                 return;
214         }
215
216         my $vals = $sock->getval(%$id);
217
218         if (! $vals) {
219                 print STDERR "socket error: " . $sock->{'error'} . $/;
220                 return;
221         }
222
223         foreach my $key (keys %$vals) {
224                 print "\t$key: $vals->{$key}\n";
225         }
226         return 1;
227 }
228
229 =item B<FLUSH> [B<timeout>=I<$timeout>] [B<plugin>=I<$plugin>[ ...]]
230
231 =cut
232
233 sub flush {
234         my $sock = shift || return;
235         my $line = shift;
236
237         my $res;
238
239         if (! $line) {
240                 $res = $sock->flush();
241         }
242         else {
243                 my %args = ();
244
245                 foreach my $i (split m/ /, $line) {
246                         my ($option, $value) = $i =~ m/^([^=]+)=(.+)$/;
247                         next if (! ($option && $value));
248
249                         if ($option eq "plugin") {
250                                 push @{$args{"plugins"}}, $value;
251                         }
252                         elsif ($option eq "timeout") {
253                                 $args{"timeout"} = $value;
254                         }
255                         elsif ($option eq "identifier") {
256                                 my $id = getid (\$value);
257                                 if (!$id)
258                                 {
259                                         print STDERR "Not a valid identifier: \"$value\"\n";
260                                         next;
261                                 }
262                                 push @{$args{"identifier"}}, $id;
263                         }
264                         else {
265                                 print STDERR "Invalid option \"$option\".\n";
266                                 return;
267                         }
268                 }
269
270                 $res = $sock->flush(%args);
271         }
272
273         if (! $res) {
274                 print STDERR "socket error: " . $sock->{'error'} . $/;
275         }
276         return $res;
277 }
278
279 =item B<LISTVAL>
280
281 =cut
282
283 sub listval {
284         my $sock = shift || return;
285
286         my @res;
287
288         @res = $sock->listval();
289
290         if (! @res) {
291                 print STDERR "socket error: " . $sock->{'error'} . $/;
292                 return;
293         }
294
295         foreach my $ident (@res) {
296                 print $ident->{'time'} . " " . putid($ident) . $/;
297         }
298         return 1;
299 }
300
301 =item B<PUTNOTIF> [[B<severity>=I<$severity>] [B<message>=I<$message>] [ ...]]
302
303 =cut
304
305 sub putnotif {
306         my $sock = shift || return;
307         my $line = shift || return;
308
309         my $ret;
310
311         my (%values) = ();
312         foreach my $i (split m/ /, $line) {
313                 my($key,$val) = split m/=/, $i, 2;
314                 if ($key && $val) {
315                         $values{$key} = $val;
316                 }
317                 else {
318                         $values{'message'} .= ' '.$key;
319                 }
320         }
321         $values{'time'} ||= time();
322
323         $ret = $sock->putnotif(%values);
324         if (! $ret) {
325                 print STDERR "socket error: " . $sock->{'error'} . $/;
326         }
327         return $ret;
328 }
329
330 =back
331
332 These commands follow the exact same syntax as described in
333 L<collectd-unixsock(5)>.
334
335 =head1 SEE ALSO
336
337 L<collectd(1)>, L<collectd-unisock(5)>
338
339 =head1 AUTHOR
340
341 Written by Sebastian Harl E<lt>sh@tokkee.orgE<gt>.
342
343 B<collectd> has been written by Florian Forster and others.
344
345 =head1 COPYRIGHT
346
347 Copyright (C) 2007 Sebastian Harl.
348
349 This program is free software; you can redistribute it and/or modify it under
350 the terms of the GNU General Public License as published by the Free Software
351 Foundation; only version 2 of the License is applicable.
352
353 =cut
354
355 # vim: set sw=4 ts=4 tw=78 noexpandtab :