librato-metrics-taps 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ java.lang:type=ClassLoading:
2
+ LoadedClassCount:
3
+ TotalLoadedClassCount: counter
4
+ UnloadedClassCount:
5
+ java.lang:type=GarbageCollector,name=ConcurrentMarkSweep:
6
+ CollectionCount: counter
7
+ CollectionTime: counter
8
+ java.lang:type=GarbageCollector,name=ParNew:
9
+ CollectionCount: counter
10
+ CollectionTime: counter
11
+ java.lang:type=Memory:
12
+ HeapMemoryUsage:
13
+ NonHeapMemoryUsage:
14
+ java.lang:type=MemoryPool,name=CMS Old Gen:
15
+ CollectionUsage:
16
+ PeakUsage:
17
+ Usage:
18
+ java.lang:type=MemoryPool,name=CMS Perm Gen:
19
+ CollectionUsage:
20
+ PeakUsage:
21
+ Usage:
File without changes
@@ -0,0 +1,24 @@
1
+ __LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), '../..'))
2
+
3
+ $LOAD_PATH.unshift __LIB_DIR__ unless
4
+ $LOAD_PATH.include?(__LIB_DIR__) ||
5
+ $LOAD_PATH.include?(File.expand_path(__LIB_DIR__))
6
+
7
+ require 'rubygems'
8
+ require 'jmx4r'
9
+ require 'librato/metrics'
10
+ require 'trollop'
11
+
12
+ module Librato
13
+ module Metrics
14
+ module Taps
15
+
16
+ def self.version
17
+ File.read(File.join(File.dirname(__FILE__), '../../../VERSION')).chomp
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'librato/metrics/taps/jmx.rb'
24
+ require 'librato/metrics/taps/publisher.rb'
@@ -0,0 +1,141 @@
1
+ module Librato
2
+ module Metrics
3
+ module Taps
4
+ module JMX
5
+ class << self
6
+ def connect!(host = 'localhost', port = 3000, username = nil, password = nil)
7
+ begin
8
+ ::JMX::MBean.establish_connection :host => host, :port => port, :username => username, :pasword => password
9
+ rescue => err
10
+ return false
11
+ end
12
+ @connected = true
13
+ end
14
+
15
+ def match_beans(beans)
16
+ raise "Not connected" unless @connected || connect!
17
+
18
+ begin
19
+ b = ::JMX::MBean.find_all_by_name(beans.to_s)
20
+ rescue => err
21
+ return nil
22
+ end
23
+
24
+ b.collect { |bean| bean.object_name.to_s }
25
+ end
26
+
27
+ #
28
+ # Retrieves a list of JMX attributes converted to
29
+ # gauges and counters.
30
+ #
31
+ # Argument options:
32
+ #
33
+ # beans = ['bean1', 'bean2']
34
+ # Will retrieve each attribute of each beanname as
35
+ # a gauge with a count value of 1.
36
+ #
37
+ # beans = {'bean1' => ['attr1', 'attr2'], 'bean2' => true}
38
+ # Will retrieve only the specified attributes for each
39
+ # bean name. To retrieve all attributes, simply set
40
+ # to true instead of a list.
41
+ #
42
+ # beans = {'bean1' => {'attr' => 'gauge',
43
+ # 'attr2' => 'counter'}}
44
+ # Will retrieve the specified beans and their
45
+ # attributes like the example above, but with the
46
+ # ability to specify how each attribute is returned. By
47
+ # default, all attributes are returned as
48
+ # gauges. However, if you set the attribute name to
49
+ # 'counter', it will return the attribute as a counter.
50
+ #
51
+ #
52
+ def retrieve(bean_names, ignore_missing = false)
53
+ raise "Not connected" unless @connected || connect!
54
+
55
+ gauges = {}
56
+ counters = {}
57
+ (bean_names.keys rescue bean_names).each do |bean|
58
+ begin
59
+ b = ::JMX::MBean.find_by_name(bean.to_s)
60
+ rescue
61
+ if ignore_missing
62
+ next
63
+ else
64
+ raise "No such bean: #{bean}"
65
+ end
66
+ end
67
+
68
+ if bean_names.respond_to?(:keys) &&
69
+ ( bean_names[bean].class == Array ||
70
+ bean_names[bean].class == Hash)
71
+ attrs = bean_names[bean]
72
+ else
73
+ attrs = b.attributes.keys
74
+ end
75
+
76
+ attrs.each do |attr|
77
+ attrname = attr.first
78
+ begin
79
+ value = b.send(snake_case(attrname.to_s))
80
+ rescue
81
+ if ignore_missing
82
+ value = nil
83
+ else
84
+ raise "Bean #{bean} has no such attribute: #{attrname}"
85
+ end
86
+ end
87
+
88
+ # Skip attributes without a value
89
+ next unless value
90
+
91
+ # Take a look at the type and only handle types that we know how to handle
92
+ if value.kind_of? Java::JavaxManagementOpenmbean::CompositeDataSupport
93
+ # If this is a composit data type loop through the elements assuming they are numbers
94
+ value.each do |value_subkey, value_subvalue|
95
+ if value_subvalue.kind_of? Numeric
96
+ if attrs.respond_to?(:keys) && "#{attr.last}".downcase == 'counter'
97
+ counters[metric_name(bean, attrname + "." + value_subkey)] = value_subvalue.to_i
98
+ else
99
+ gauges[metric_name(bean, attrname + "." + value_subkey)] = value_subvalue
100
+ end
101
+ else
102
+ raise "The subvalue \"#{value_subkey}\" of value \"#{value}\" in Bean \"#{bean}\" is of type \"#{value_subvalue.class}\" and I only know how to handle numbers.\n"
103
+ end
104
+ end
105
+ elsif value.kind_of? Numeric
106
+ # Skip bogus values
107
+ next if value.to_f.nan? || value.to_f.infinite?
108
+
109
+ # If this is a number go ahead and submit it as either a counter or gauge
110
+ # depending on what we set in the attributes
111
+ if attrs.respond_to?(:keys) && "#{attr.last}".downcase == 'counter'
112
+ counters[metric_name(bean, attrname)] = value.to_i
113
+ else
114
+ gauges[metric_name(bean, attrname)] = value
115
+ end
116
+ else
117
+ raise "The value \"#{value}\" of Bean \"#{bean}\" is of type \"#{value.class}\" and I do not know how to handle that type.\n"
118
+ end
119
+ end
120
+ end
121
+
122
+ [counters, gauges]
123
+ end
124
+
125
+ # From ActiveSupport
126
+ def snake_case(camel_cased_word)
127
+ camel_cased_word.to_s.gsub(/::/, '/').
128
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
129
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
130
+ tr("-", "_").
131
+ downcase
132
+ end
133
+
134
+ def metric_name(bean_name, attr_name)
135
+ "#{bean_name.gsub('=', ':').gsub(/[, ]/, '_')}::#{attr_name}"
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,88 @@
1
+ module Librato
2
+ module Metrics
3
+ module Taps
4
+ class Publisher
5
+ SOURCE_NAME_REGEX = /^[-A-Za-z0-9_.]{1,255}$/
6
+
7
+ @url = nil
8
+ @user = nil
9
+ @passwd = nil
10
+
11
+ DEFAULT_BATCH_SIZE = 300
12
+
13
+ def initialize(opts)
14
+ unless !opts[:source] || opts[:source] =~ SOURCE_NAME_REGEX
15
+ raise "Invalid source"
16
+ end
17
+
18
+ unless !opts[:prefix] || opts[:prefix] =~ SOURCE_NAME_REGEX
19
+ raise "Invalid prefix"
20
+ end
21
+
22
+ @client = Librato::Metrics::Client.new
23
+ @client.authenticate(opts[:email], opts[:token])
24
+ @client.api_endpoint = opts[:metrics_url]
25
+ @client.agent_identifier("librato-metrics-tap-jmxbeans/%s" %
26
+ [Taps.version])
27
+
28
+ qparams = {
29
+ :autosubmit_count => DEFAULT_BATCH_SIZE
30
+ }
31
+
32
+ [:prefix, :source].each do |o|
33
+ qparams[o] = opts[o] if opts[o]
34
+ end
35
+
36
+ @queue_params = qparams
37
+ end
38
+
39
+ # Post metrics to metrics service
40
+ #
41
+ # counters:
42
+ # {:my-counter-1 => 4, :my-counter-2 => 6, ...}
43
+ #
44
+ # gauges:
45
+ # {:my-gauge-1 => 4, ...}
46
+ # or
47
+ # {:my-gauge-2 => {:value => 5, :count => 4,
48
+ # :min => 3, :max => 6, ...}}
49
+ # or mix-match.
50
+ def post(counters, gauges, params = {})
51
+ mt = params[:measure_time] || now_secs
52
+ q = @client.new_queue(@queue_params)
53
+
54
+ if counters.length > 0
55
+ counters.each_pair do |k, v|
56
+ q.add k => {:type => :counter, :value => v, :measure_time => mt}
57
+ end
58
+ end
59
+
60
+ if gauges.length > 0
61
+ params[:gauges] = {}
62
+ gauges.each_pair do |k, v|
63
+ if v.respond_to?(:keys)
64
+ q.add k => v.merge(:measure_time => mt)
65
+ else
66
+ q.add k => {:value => Float(v), :measure_time => mt}
67
+ end
68
+ end
69
+ end
70
+
71
+ begin
72
+ q.submit
73
+ rescue => err
74
+ # XXX: ever get here? what about auto-submit?
75
+ puts "Failed to publish stats (unknown exception): #{err.inspect}"
76
+ return false
77
+ end
78
+
79
+ true
80
+ end
81
+
82
+ def now_secs
83
+ Time.now.tv_sec
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{librato-metrics-taps}
5
+ s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).chomp
6
+
7
+ s.authors = [%q{Librato, Inc.}]
8
+ s.date = %q{2012-05-15}
9
+ s.description = %q{Taps for extracting metrics data and publishing to Librato Metrics}
10
+ s.email = %q{mike@librato.com}
11
+ s.summary = %q{Librato Metrics Taps}
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.homepage = %q{http://github.com/librato/librato-metrics-taps}
19
+ s.licenses = [%q{MIT}]
20
+
21
+ s.add_runtime_dependency(%q<jmx4r>, ["= 0.1.4"])
22
+ s.add_runtime_dependency(%q<librato-metrics>, ["~> 1.0.4"])
23
+ s.add_runtime_dependency(%q<trollop>, ["= 1.16.2"])
24
+ s.add_runtime_dependency(%q<jruby-openssl>, ["= 0.8.5"])
25
+
26
+ s.add_development_dependency(%q<rake>, [">= 0"])
27
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
28
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
29
+ end
30
+
@@ -0,0 +1,657 @@
1
+ /* Print Debug Info. Uncomment the #define below to print debugging to stderr */
2
+
3
+ //#define DEBUG
4
+
5
+ /* Number of seconds to timeout */
6
+ #define CONNECT_TIMEOUT 5
7
+ #define MAX_RETRIES 5
8
+ /******************************************************************************
9
+
10
+ Esensors EM01 Plugin.
11
+ Description: This plugin is written mainly for Nagios, but can be
12
+ easily used for other software too.
13
+
14
+ This program is free software; you can redistribute it and/or modify
15
+ it under the terms of the GNU General Public License as published by
16
+ the Free Software Foundation; either version 2 of the License, or
17
+ (at your option) any later version.
18
+
19
+ This program is distributed in the hope that it will be useful,
20
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ GNU General Public License for more details.
23
+
24
+ You should have received a copy of the GNU General Public License
25
+ along with this program; if not, write to the Free Software
26
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27
+
28
+ Credits:
29
+ Duncan Robertson [Duncan.Robertson@vsl.com.au] - 64bits fix
30
+ Ali Rahimi [ali@XCF.Berkeley.EDU] - Sockets code
31
+ David W. Deley [deleyd@cox.net] - Random numbers
32
+
33
+ $Id: check_em01.c,v 2.1 3:47 PM 1/26/2009 $
34
+
35
+ ******************************************************************************/
36
+ /** This value is to multiple the Voltage value detected by a fixed constant. (
37
+ * i.e. if you are using a regulator supply to measure AC voltage)
38
+ */
39
+ const float VOLTAGE_MULTIPLIER = 1.0;
40
+ const float VOLTAGE_OFFSET = 0.0;
41
+
42
+ /**
43
+ * Tells the plugin to reset contact closure on trigger. Change to 0 to turn this off.
44
+ */
45
+ const int RESETCONTACT = 1;
46
+
47
+
48
+ const int DEFAULTPORT = 80;
49
+ const char *progname = "check_em01";
50
+ const char *revision = "$Revision: 2.1 $";
51
+ const char *copyright = "2009";
52
+ const char *email = "techhelp@eEsensors.com";
53
+
54
+ typedef int SOCKET;
55
+ typedef enum {E_NET_ERRNO=-1, E_NET_OK=0} NetErrnoType;
56
+
57
+ #include <stdio.h>
58
+ #include <stdlib.h>
59
+ #include <sys/types.h>
60
+ #include <sys/socket.h>
61
+ #include <netinet/in.h>
62
+ #include <arpa/inet.h>
63
+ #include <netdb.h>
64
+ #include <sys/ioctl.h>
65
+ #include <string.h>
66
+ #include <errno.h>
67
+ #include <signal.h>
68
+ #include <time.h>
69
+
70
+ void INThandler(int);
71
+
72
+ static NetErrnoType net_errno = E_NET_OK;
73
+ static int saved_errno = 0;
74
+ static SOCKET s;
75
+
76
+
77
+ /* Translations between ``net_errno'' values and human readable strings.
78
+ */
79
+ static const char *net_syserrlist[] = {
80
+ "All was chill"
81
+ };
82
+
83
+
84
+
85
+
86
+ #ifdef STRERROR_NOT_DEFINED
87
+ const char *strerror(int errno) { return sys_errlist[errno]; }
88
+ #endif
89
+
90
+ static void NetSetErrno(NetErrnoType e)
91
+ {
92
+ if(e == E_NET_ERRNO)saved_errno = errno;
93
+ net_errno = e;
94
+ }
95
+
96
+
97
+
98
+ /* NetErrStr()
99
+ *--------------------------------------------------------------------
100
+ * Returns a diagnostic message for the last failure.
101
+ */
102
+ const char *NetErrStr()
103
+ {
104
+ return net_errno==E_NET_ERRNO ? strerror(saved_errno) :
105
+ net_syserrlist[net_errno];
106
+ }
107
+
108
+ /* NetErrNo()
109
+ *--------------------------------------------------------------------
110
+ * Returns a diagnostic number for the last failure.
111
+ */
112
+ NetErrnoType NetErrNo()
113
+ {
114
+ return net_errno;
115
+ }
116
+
117
+ /* NetMakeContact()
118
+ *--------------------------------------------------------------------
119
+ * Makes a tcp connection to a host:port pair.
120
+ *--------------------------------------------------------------------
121
+ * ``Hostname'' can either be in the form of a hostname or an IP address
122
+ * represented as a string. If the hostname is not found as it is,
123
+ * ``hostname'' is assumed to be an IP address, and it is treated as such.
124
+ *
125
+ * If the lookup succeeds, a TCP connection is established with the
126
+ * specified ``port'' number on the remote host and a stream socket is
127
+ * returned.
128
+ *
129
+ * On any sort of error, an error code can be obtained with @NetErrNo()
130
+ * and a message with @NetErrStr().
131
+ */
132
+ SOCKET
133
+ NetMakeContact(const char *hname, int port)
134
+ {
135
+ int fd;
136
+ struct sockaddr_in addr;
137
+ struct hostent *hent;
138
+
139
+ fd = socket(AF_INET, SOCK_STREAM, 0);
140
+ if(fd == -1)
141
+ {
142
+ NetSetErrno(E_NET_ERRNO);
143
+ return -1;
144
+ }
145
+
146
+
147
+ hent = gethostbyname(hname);
148
+ if(hent == NULL)
149
+ addr.sin_addr.s_addr = inet_addr(hname);
150
+ else
151
+ memcpy(&addr.sin_addr, hent->h_addr, hent->h_length);
152
+ addr.sin_family = AF_INET;
153
+ addr.sin_port = htons(port);
154
+
155
+ #ifdef DEBUG
156
+ fprintf(stderr, "Creating Connection... ");
157
+ #endif
158
+
159
+ if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)))
160
+ {
161
+ NetSetErrno(E_NET_ERRNO);
162
+ return -1;
163
+ }
164
+
165
+ NetSetErrno(E_NET_OK);
166
+ return fd;
167
+ }
168
+
169
+
170
+
171
+ /*********************************************************************************/
172
+
173
+ float Exp10(int n)
174
+ {
175
+ int i;
176
+ float result = 1;
177
+ for(i =n; i; i--)
178
+ result *= 10;
179
+ return result;
180
+ }
181
+
182
+ float myatof(const char *s)
183
+ {
184
+ float result;
185
+ int val = 0, dec = 0, n = 0;;
186
+ int neg = 0;
187
+
188
+ /* skip white space */
189
+ while (*s == ' ' || *s == '\t') {
190
+ s++;
191
+ }
192
+
193
+ if (*s == '-') {
194
+ neg = 1;
195
+ s++;
196
+ } else if (*s == '+') {
197
+ s++;
198
+ }
199
+ while (*s >= '0' && *s <= '9') {
200
+ val *= 10;
201
+ val += *s++ - '0';
202
+ }
203
+ result = val;
204
+
205
+ if(*s == '.') {
206
+ *s++;
207
+ while (*s >= '0' && *s <= '9') {
208
+ dec *= 10;
209
+ dec += *s++ - '0';
210
+ n++;
211
+ }
212
+
213
+ if(n)
214
+ result += dec/Exp10(n);
215
+ }
216
+
217
+ if (neg) {
218
+ result = -result;
219
+ }
220
+
221
+ return result;
222
+ }
223
+
224
+ /*
225
+ This function is an abstraction layer between app and sockets.
226
+ Currently, it only passes down all the arguments it receives from
227
+ app. However, in the future, it will be easier to change
228
+ sockets library and not affect the main app code at all.
229
+ */
230
+ SOCKET connectWebsensor (char* hostname, int port){
231
+ //SOCKET s;
232
+ fd_set readfds;
233
+
234
+ int i;
235
+ s = NetMakeContact(hostname,port);
236
+ if(s==-1) {
237
+ return -1;
238
+ }
239
+ else{
240
+ /* Make the socket non-blocking */
241
+ ioctl(s, FIONBIO, 1);
242
+ FD_ZERO(&readfds);
243
+ FD_SET(0, &readfds);
244
+ FD_SET(s, &readfds);
245
+ return s;
246
+
247
+ }
248
+ }
249
+
250
+ /*
251
+ This function is called when a timeout has occurred.
252
+ It shutsdown the socket and allows the main loop to continue.
253
+ */
254
+ void INThandler(int sig)
255
+ {
256
+ signal(SIGALRM, SIG_IGN);
257
+ shutdown(s, SHUT_RDWR);
258
+ alarm(CONNECT_TIMEOUT);
259
+ signal(SIGALRM, INThandler);
260
+ }
261
+
262
+
263
+ main(int argc, char **argv)
264
+ {
265
+ int l, retry, i, random_backoff;
266
+ long ms;
267
+ float data;
268
+ char iobuf[1024], timestr[32], datachar[7];
269
+ char* pos;
270
+ char rcvd_checksum, calc_checksum;
271
+ time_t start_time, cur_time;
272
+ double r, x;
273
+ SOCKET contacts;
274
+
275
+ progname = argv[0];
276
+ if(argc < 2 || strcmp(argv[1],"--help") == 0) {
277
+ print_help();
278
+ return(3);
279
+ }
280
+
281
+ time(&cur_time);
282
+ sprintf(timestr, "%s", ctime(&cur_time));
283
+
284
+ signal(SIGALRM, INThandler);
285
+ alarm(CONNECT_TIMEOUT);
286
+
287
+ for(retry = 0; retry < MAX_RETRIES; retry++){
288
+
289
+ srand((unsigned int)time(NULL));
290
+
291
+ r = ( (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
292
+ x = (r * 20);
293
+ random_backoff = (int) x;
294
+
295
+ #ifdef DEBUG
296
+ fprintf(stderr, "Sleeping: %d ", 100+random_backoff);
297
+ #endif
298
+ for(ms=0; ms < 100+random_backoff; ms++){
299
+ usleep(5000);
300
+ }
301
+
302
+
303
+ /* make connection to websensor */
304
+ s = connectWebsensor(argv[1], DEFAULTPORT);
305
+ #ifdef DEBUG
306
+ fprintf(stderr, "Socket created ");
307
+ #endif
308
+
309
+ if(NetErrNo() != 0){
310
+ #ifdef DEBUG
311
+ fprintf(stderr, "Could not connect to Websensor because %s, will retry %d more times.\n", NetErrStr(), 10-retry);
312
+ #endif
313
+ shutdown(s, SHUT_RDWR);
314
+ continue;
315
+ }
316
+
317
+
318
+ /* send HTTP GET request to obtain data */
319
+ if(argc>2){
320
+
321
+ switch (toupper(argv[2][0])){
322
+ case 'R':
323
+ write(s, "GET /index.html?eR HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 77);
324
+ break;
325
+
326
+ case 'V':
327
+ write(s, "GET /index.html?eV HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 77);
328
+ break;
329
+
330
+ default:
331
+ write(s, "GET /index.html?em123456 HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 83);
332
+ break;
333
+ }
334
+ }
335
+
336
+ else{ // Not enough arguments from command line. Use default websensor command.
337
+ write(s, "GET /index.html?em123456 HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 83);
338
+ }
339
+
340
+ #ifdef DEBUG
341
+ fprintf(stderr, "Wrote to Socket ");
342
+ #endif
343
+
344
+ l = read(s, iobuf, sizeof(iobuf));
345
+
346
+ #ifdef DEBUG
347
+ fprintf(stderr, "Read from socket ");
348
+ #endif
349
+
350
+ /* No data returned from websensor. Will retry again. */
351
+
352
+ if(l<=0){
353
+ #ifdef DEBUG
354
+ fprintf(stderr, "No Data Read, will retry %d more times.\n", MAX_RETRIES-retry);
355
+ #endif
356
+ shutdown(s, SHUT_RDWR);
357
+ continue;
358
+ }
359
+
360
+ pos = strstr(iobuf, "<body>");
361
+ if(pos == 0){
362
+ printf("Invalid data received.\n");
363
+ return(3);
364
+ }
365
+
366
+
367
+
368
+ //Search for the sensor data string
369
+ pos = strstr(iobuf, "TF:");
370
+ if(pos == 0){
371
+ pos = strstr(iobuf, "TC:");
372
+ if(pos == 0){
373
+ #ifdef DEBUG
374
+ fprintf(stderr, "Using default parsing parameters.\n");
375
+ #endif
376
+ pos=&iobuf[167];
377
+ }
378
+ }
379
+
380
+ if(argc>2 && toupper(argv[2][0]) == 'V'){ //If the command was for voltage measurement, try looking for the string 'RV' instead.
381
+ pos = strstr(iobuf, "RV");
382
+ if(pos == 0 || pos[1] != 'V'){
383
+ #ifdef DEBUG
384
+ fprintf(stderr, "Invalid Data Received. Will retry %d more times.\n", MAX_RETRIES-retry);
385
+ #endif
386
+ shutdown(s, SHUT_RDWR);
387
+ continue;
388
+ }
389
+ else{
390
+ pos = pos - 2;
391
+ break; //Voltage data looks good
392
+ }
393
+ }
394
+
395
+ pos = pos - 2;
396
+ /* Unsupported command. OBSOLETE CODE */
397
+ if(pos[0] == '#'){
398
+ printf("Invalid Command. Option %c selected may not be available for this websensor.\n", argv[2][0]);
399
+ return 3;
400
+ }
401
+
402
+ /* The http data is not properly formatted. */
403
+ if(pos[2] != 'T' && pos[2] != 'R' && pos[1] != 'v'){
404
+ #ifdef DEBUG
405
+ fprintf(stderr, "Data input incorrect, will retry %d more times.\n", MAX_RETRIES-retry);
406
+ #endif
407
+ shutdown(s, SHUT_RDWR);
408
+ continue;
409
+ }
410
+ else{
411
+ break; /* All data looks good. Break out of loop. */
412
+ }
413
+ }
414
+
415
+ time(&cur_time);
416
+ sprintf(timestr, "%s", ctime(&cur_time));
417
+
418
+ /* Retried 3 times earlier and still no good data. Time to exit */
419
+ if(retry >= MAX_RETRIES){
420
+ printf("NO DATA\n");
421
+ return 3;
422
+ }
423
+ else{
424
+ shutdown(s, SHUT_RDWR);
425
+ }
426
+
427
+ if(argc > 2){
428
+ switch(toupper(argv[2][0])){
429
+
430
+
431
+ case 'G': //Cacti/RRDTool Output TempF:** Humid:**
432
+ {
433
+ strncpy(datachar, pos+5, 5);
434
+ datachar[5] = '\0';
435
+ data = myatof(datachar);
436
+ data += 0.01;
437
+ printf("Temp:%3.2f ", data);
438
+ //printf("TempUnit:%c ", pos[3]); //prints the temperature unit
439
+
440
+ strncpy(datachar, pos+13, 4);
441
+ datachar[4] = '\0';
442
+ data = myatof(datachar);
443
+ data += 0.01;
444
+ printf("Humid:%3.2f ", data);
445
+
446
+ strncpy(datachar, pos+21, 5);
447
+ datachar[5] = '\0';
448
+ data = myatof(datachar);
449
+ data += 0.01;
450
+ printf("Illum:%3.2f\n", data);
451
+
452
+ return(0);
453
+ }
454
+ break;
455
+
456
+
457
+
458
+ case 'T':
459
+ {
460
+ strncpy(datachar, pos+5, 5);
461
+ datachar[5] = '\0';
462
+ data = myatof(datachar);
463
+ data += 0.01;
464
+ if(argc != 7){
465
+ printf("(No limits specified) Temperature: %s %c | Temp%c=%3.2f\n", datachar, pos[3], pos[3], data);
466
+ return(0);
467
+ }
468
+ if(data < myatof(argv[5]) || data > myatof(argv[6])){
469
+ printf("CRITICAL ( %s< or >%s ) Temperature: %s %c | Temp%c=%3.2f\n", argv[5], argv[6], datachar, pos[3], pos[3], data);
470
+ return(2);
471
+ }
472
+ else if(data < myatof(argv[3]) || data > myatof(argv[4])){
473
+ printf("WARNING ( %s< or >%s ) Temperature: %s %c | Temp%c=%3.2f\n", argv[3], argv[4], datachar, pos[3], pos[3], data);
474
+ return(1);
475
+ }
476
+ else{
477
+ printf("OK Temperature: %s %c | Temp%c=%3.2f\n", datachar, pos[3], pos[3], data);
478
+ return(0);
479
+ }
480
+ }
481
+ break;
482
+
483
+ case 'H':
484
+ {
485
+ strncpy(datachar, pos+13, 4);
486
+ datachar[4] = '\0';
487
+ data = myatof(datachar);
488
+ data += 0.01;
489
+ if(argc != 7){
490
+ printf("(No limits specified) %s% | Humid=%3.2f\n", datachar, data);
491
+ return(0);
492
+ }
493
+ if(data < myatof(argv[5]) || data > myatof(argv[6])){
494
+ printf("CRITICAL ( %s< or >%s ) Humidity: %s% | Humid=%3.2f\n", argv[5], argv[6], datachar, data);
495
+ return(2);
496
+ }
497
+ else if(data < myatof(argv[3]) || data > myatof(argv[4])){
498
+ printf("WARNING ( %s< or >%s ) Humidity: %s% | Humid=%3.2f\n", argv[3], argv[4], datachar, data);
499
+ return(1);
500
+ }
501
+ else{
502
+ printf("OK Humidity: %s% | Humid=%3.2f\n", datachar, data);
503
+ return(0);
504
+ }
505
+ }
506
+ break;
507
+
508
+ case 'I':
509
+ {
510
+ strncpy(datachar, pos+21, 5);
511
+ datachar[5] = '\0';
512
+ data = myatof(datachar);
513
+ data += 0.01;
514
+ if(argc != 7){
515
+ printf("(No limits specified) Illumination: %s | Illum=%3.2f\n", datachar, data);
516
+ return(0);
517
+ }
518
+ if(data < myatof(argv[5]) || data > myatof(argv[6])){
519
+ printf("CRITICAL ( %s< or >%s ) Illumination: %s | Illum=%3.2f\n", argv[5], argv[6], datachar, data);
520
+ return(2);
521
+ }
522
+ else if(data < myatof(argv[3]) || data > myatof(argv[4])){
523
+ printf("WARNING ( %s< or >%s ) Illumination: %s | Illum=%3.2f\n", argv[3], argv[4], datachar, data);
524
+ return(1);
525
+ }
526
+ else{
527
+ printf("OK Illumination: %s | Illum=%3.2f\n", datachar,data);
528
+ return(0);
529
+ }
530
+ }
531
+ break;
532
+
533
+ case 'C':
534
+ {
535
+ if(iobuf[160] == 'W'){
536
+ printf("OK Contacts Close. | Contacts=0\n");
537
+ return(0);
538
+ }
539
+ else if(iobuf[160] == 'N'){
540
+ printf("CRITICAL Contacts Open! | Contacts=1\n");
541
+
542
+ /* Reset the Contact Closure back to Closed */
543
+ if(RESETCONTACT == 1){
544
+ contacts = connectWebsensor(argv[1], DEFAULTPORT);
545
+ if(NetErrNo() != 0){
546
+ fprintf(stderr, "Could not reset contact closure", NetErrStr());
547
+ shutdown(contacts, SHUT_RDWR);
548
+ return(2);
549
+ }
550
+ write(contacts, "GET /index.html?eL HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 77);
551
+ l = read(contacts, iobuf, sizeof(iobuf));
552
+ //printf("%s", iobuf);
553
+ shutdown(contacts, SHUT_RDWR);
554
+ }
555
+ return(2);
556
+ }
557
+ else{
558
+ printf("WARNING Unknown status. Try Reset Device.\n");
559
+ return(1);
560
+ }
561
+ }
562
+ break;
563
+
564
+ case 'R':
565
+ {
566
+ strncpy(datachar, pos+4, 6);
567
+ datachar[6] = '\0';
568
+ data = myatof(datachar);
569
+ data += 0.01;
570
+ if(argc != 7){
571
+ printf("(No limits specified) Ext. Temperature: %s %c | XTemp%c=%3.2f\n", datachar, pos[3], pos[3], data);
572
+ return(0);
573
+ }
574
+ if(data < myatof(argv[5]) || data > myatof(argv[6])){
575
+ printf("CRITICAL ( %s< or >%s ) Ext. Temperature: %s %c | XTemp%c=%3.2f\n", argv[5], argv[6], datachar, pos[3], pos[3], data);
576
+ return(2);
577
+ }
578
+ else if(data < myatof(argv[3]) || data > myatof(argv[4])){
579
+ printf("WARNING ( %s< or >%s ) Ext. Temperature: %s %c | XTemp%c=%3.2f\n", argv[3], argv[4], datachar, pos[3], pos[3], data);
580
+ return(1);
581
+ }
582
+ else{
583
+ printf("OK Ext. Temperature: %s %c | XTemp%c=%3.2f\n", datachar, pos[3], pos[3], data);
584
+ return(0);
585
+ }
586
+ }
587
+ break;
588
+
589
+ case 'V':
590
+ {
591
+ strncpy(datachar, pos+21, 5);
592
+ datachar[5] = '\0';
593
+ data = myatof(datachar);
594
+ data = (data * VOLTAGE_MULTIPLIER) + VOLTAGE_OFFSET;
595
+ data += 0.01;
596
+ if(data <= VOLTAGE_OFFSET+(0.5*VOLTAGE_MULTIPLIER)){ //Anything less than VOLTAGE_OFFSET AC should be considered as zero
597
+ data = 0.00;
598
+ }
599
+
600
+ if(argc != 7){
601
+ printf("(No limits specified) Voltage: %3.2f V | Voltage=%3.2f\n", data,data);
602
+ return(0);
603
+ }
604
+ if(data < myatof(argv[5]) || data > myatof(argv[6])){
605
+ printf("CRITICAL ( %s< or >%s ) Voltage: %3.2f V | Voltage=%3.2f\n", argv[5], argv[6], data, data);
606
+ return(2);
607
+ }
608
+ else if(data < myatof(argv[3]) || data > myatof(argv[4])){
609
+ printf("WARNING ( %s< or >%s ) Voltage: %3.2f V | Voltage=%3.2f\n", argv[3], argv[4], data, data);
610
+ return(1);
611
+ }
612
+
613
+ else{
614
+ printf("OK Voltage: %3.2f V | Voltage=%3.2f\n", data,data);
615
+ return(0);
616
+ }
617
+ }
618
+
619
+ default :
620
+ printf("Please choose only 'T', 'H', 'I', 'R', 'V' or 'C'.\n Please refer to README for further instructions.\n");
621
+ break;
622
+ }
623
+ }
624
+ else{
625
+ iobuf[195] = 0;
626
+ fprintf(stderr, "2");
627
+ printf("%s\t%s", pos+2, timestr);
628
+ fprintf(stderr, "3");
629
+ }
630
+
631
+ return 0;
632
+ }
633
+
634
+ int print_help (void)
635
+ {
636
+ fprintf (stdout, "Copyright (c) 2005-2009 Esensors, Inc <TechHelp@eEsensors.com>\n");
637
+ fprintf (stdout,("This plugin is written mainly for Nagios/Cacti/RRDTool, but can work standalone too. \nIt returns the HVAC data from the EM01 Websensor\n\n"));
638
+ print_usage ();
639
+ return 0;
640
+ }
641
+
642
+
643
+
644
+ int print_usage (void)
645
+ {
646
+ fprintf(stdout, "usage: \n %s [hostname] [T/H/I] [LowWarning HighWarning LowCritical HighCritical]\n\n", progname);
647
+ fprintf(stdout, "Only the hostname is mandatory. The rest of the arguments are optional\n");
648
+ fprintf(stdout, "T is for Temperature data, H for Humidity Data and I for Illumination data\nExamples:\n");
649
+ fprintf(stdout, "This will return all HVAC data: \n %s 192.168.0.2\n\n", progname);
650
+ fprintf(stdout, "This will return only Illumination data: \n %s 192.168.0.2 I\n\n", progname);
651
+ fprintf(stdout, "This will return temperature data with status: \n %s 192.168.0.2 T 65 85 60 90\n\n", progname);
652
+ fprintf(stdout, "This will return humidity data with status: \n %s 192.168.0.2 H 25.5 50 15 70.5\n\n", progname);
653
+ fprintf(stdout, "This will return Cacti/RRDTool format: \n %s 192.168.0.2 G\n\n", progname);
654
+ fprintf(stdout, "For further information, please refer to the README file included with the package\n");
655
+ fprintf(stdout, "or available for download from http://www.eesensors.com\n");
656
+ return 0;
657
+ }