librato-metrics-taps 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }