Merge pull request #2137 from maryamtahhan/feat_ovs_stats
[collectd.git] / src / nut.c
1 /**
2  * collectd - src/nut.c
3  * Copyright (C) 2007       Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "plugin.h"
31
32 #include <upsclient.h>
33
34 #if HAVE_UPSCONN_T
35 typedef UPSCONN_t collectd_upsconn_t;
36 #elif HAVE_UPSCONN
37 typedef UPSCONN collectd_upsconn_t;
38 #else
39 #error "Unable to determine the UPS connection type."
40 #endif
41
42 struct nut_ups_s;
43 typedef struct nut_ups_s nut_ups_t;
44 struct nut_ups_s {
45   collectd_upsconn_t *conn;
46   char *upsname;
47   char *hostname;
48   int port;
49   nut_ups_t *next;
50 };
51
52 static nut_ups_t *upslist_head = NULL;
53
54 static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
55 static int read_busy = 0;
56
57 static const char *config_keys[] = {"UPS", "FORCESSL", "VERIFYPEER", "CAPATH"};
58 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
59 static int force_ssl = 0;   // Initialized to default of 0 (false)
60 static int verify_peer = 0; // Initialized to default of 0 (false)
61 static char *ca_path = NULL;
62
63 static void free_nut_ups_t(nut_ups_t *ups) {
64   if (ups->conn != NULL) {
65     upscli_disconnect(ups->conn);
66     sfree(ups->conn);
67   }
68   sfree(ups->hostname);
69   sfree(ups->upsname);
70   sfree(ups);
71 } /* void free_nut_ups_t */
72
73 static int nut_add_ups(const char *name) {
74   nut_ups_t *ups;
75   int status;
76
77   DEBUG("nut plugin: nut_add_ups (name = %s);", name);
78
79   ups = calloc(1, sizeof(*ups));
80   if (ups == NULL) {
81     ERROR("nut plugin: nut_add_ups: calloc failed.");
82     return (1);
83   }
84
85   status = upscli_splitname(name, &ups->upsname, &ups->hostname, &ups->port);
86   if (status != 0) {
87     ERROR("nut plugin: nut_add_ups: upscli_splitname (%s) failed.", name);
88     free_nut_ups_t(ups);
89     return (1);
90   }
91
92   if (upslist_head == NULL)
93     upslist_head = ups;
94   else {
95     nut_ups_t *last = upslist_head;
96     while (last->next != NULL)
97       last = last->next;
98     last->next = ups;
99   }
100
101   return (0);
102 } /* int nut_add_ups */
103
104 static int nut_force_ssl(const char *value) {
105   if (strcasecmp(value, "true") == 0)
106     force_ssl = 1;
107   else if (strcasecmp(value, "false") == 0)
108     force_ssl = 0; // Should already be set to 0 from initialization
109   else {
110     force_ssl = 0;
111     WARNING("nut plugin: nut_force_ssl: invalid FORCESSL value "
112             "found. Defaulting to false.");
113   }
114   return (0);
115 } /* int nut_parse_force_ssl */
116
117 static int nut_verify_peer(const char *value) {
118   if (strcasecmp(value, "true") == 0)
119     verify_peer = 1;
120   else if (strcasecmp(value, "false") == 0)
121     verify_peer = 0; // Should already be set to 0 from initialization
122   else {
123     verify_peer = 0;
124     WARNING("nut plugin: nut_verify_peer: invalid VERIFYPEER value "
125             "found. Defaulting to false.");
126   }
127   return (0);
128 } /* int nut_verify_peer */
129
130 static int nut_ca_path(const char *value) {
131   if (value != NULL && strcmp(value, "") != 0) {
132     ca_path = malloc(strlen(value) + 1);
133     strncpy(ca_path, value, (strlen(value) + 1));
134   } else {
135     ca_path = NULL; // Should alread be set to NULL from initialization
136   }
137   return (0);
138 } /* int nut_ca_path */
139
140 static int nut_config(const char *key, const char *value) {
141   if (strcasecmp(key, "UPS") == 0)
142     return (nut_add_ups(value));
143   else if (strcasecmp(key, "FORCESSL") == 0)
144     return (nut_force_ssl(value));
145   else if (strcasecmp(key, "VERIFYPEER") == 0)
146     return (nut_verify_peer(value));
147   else if (strcasecmp(key, "CAPATH") == 0)
148     return (nut_ca_path(value));
149   else
150     return (-1);
151 } /* int nut_config */
152
153 static void nut_submit(nut_ups_t *ups, const char *type,
154                        const char *type_instance, gauge_t value) {
155   value_list_t vl = VALUE_LIST_INIT;
156
157   vl.values = &(value_t){.gauge = value};
158   vl.values_len = 1;
159   sstrncpy(vl.host,
160            (strcasecmp(ups->hostname, "localhost") == 0) ? hostname_g
161                                                          : ups->hostname,
162            sizeof(vl.host));
163   sstrncpy(vl.plugin, "nut", sizeof(vl.plugin));
164   sstrncpy(vl.plugin_instance, ups->upsname, sizeof(vl.plugin_instance));
165   sstrncpy(vl.type, type, sizeof(vl.type));
166   sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
167
168   plugin_dispatch_values(&vl);
169 } /* void nut_submit */
170
171 static int nut_connect(nut_ups_t *ups) {
172 #ifdef WITH_UPSCLIENT_27
173   int status;
174   int ssl_status;
175   int ssl_flags;
176
177   if (verify_peer == 1 && force_ssl == 0) {
178     WARNING("nut plugin: nut_connect: VerifyPeer true but ForceSSL "
179             "false. Setting ForceSSL to true.");
180     force_ssl = 1;
181   }
182
183   if (verify_peer == 1 && ca_path == NULL) {
184     ERROR("nut plugin: nut_connect: VerifyPeer true but missing "
185           "CAPath value.");
186     return (-1);
187   }
188
189   if (verify_peer == 1) {
190     status = upscli_init(verify_peer, ca_path, NULL, NULL);
191
192     if (status != 1) {
193       ERROR("nut plugin: nut_connect: upscli_init (%i, %s) failed: %s",
194             verify_peer, ca_path, upscli_strerror(ups->conn));
195       upscli_cleanup();
196       return (-1);
197     }
198   } /* if (verify_peer == 1) */
199
200   if (verify_peer == 1)
201     ssl_flags = (UPSCLI_CONN_REQSSL | UPSCLI_CONN_CERTVERIF);
202   else if (force_ssl == 1)
203     ssl_flags = UPSCLI_CONN_REQSSL;
204   else
205     ssl_flags = UPSCLI_CONN_TRYSSL;
206
207   status = upscli_connect(ups->conn, ups->hostname, ups->port, ssl_flags);
208
209   if (status != 0) {
210     ERROR("nut plugin: nut_connect: upscli_connect (%s, %i) failed: %s",
211           ups->hostname, ups->port, upscli_strerror(ups->conn));
212     sfree(ups->conn);
213     upscli_cleanup();
214     return (-1);
215   } /* if (status != 0) */
216
217   INFO("nut plugin: Connection to (%s, %i) established.", ups->hostname,
218        ups->port);
219
220   // Output INFO or WARNING based on SSL and VERIFICATION
221   ssl_status = upscli_ssl(ups->conn); // 1 for SSL, 0 for not, -1 for error
222   if (ssl_status == 1 && verify_peer == 1) {
223     INFO("nut plugin: Connection is secured with SSL and certificate "
224          "has been verified.");
225   } else if (ssl_status == 1) {
226     INFO("nut plugin: Connection is secured with SSL with no verification "
227          "of server SSL certificate.");
228   } else if (ssl_status == 0) {
229     WARNING("nut plugin: Connection is unsecured (no SSL).");
230   } else {
231     ERROR("nut plugin: nut_connect: upscli_ssl failed: %s",
232           upscli_strerror(ups->conn));
233     sfree(ups->conn);
234     upscli_cleanup();
235     return (-1);
236   } /* if (ssl_status == 1 && verify_peer == 1) */
237   return (0);
238
239 #else /* #ifdef WITH_UPSCLIENT_27 */
240   int status;
241   int ssl_status;
242   int ssl_flags;
243
244   if (verify_peer == 1 || ca_path != NULL) {
245     WARNING("nut plugin: nut_connect: Dependency libupsclient version "
246             "insufficient (<2.7) for VerifyPeer support. Ignoring VerifyPeer "
247             "and CAPath.");
248   }
249
250   if (force_ssl == 1)
251     ssl_flags = UPSCLI_CONN_REQSSL;
252   else
253     ssl_flags = UPSCLI_CONN_TRYSSL;
254
255   status = upscli_connect(ups->conn, ups->hostname, ups->port, ssl_flags);
256
257   if (status != 0) {
258     ERROR("nut plugin: nut_connect: upscli_connect (%s, %i) failed: %s",
259           ups->hostname, ups->port, upscli_strerror(ups->conn));
260     sfree(ups->conn);
261     return (-1);
262   } /* if (status != 0) */
263
264   INFO("nut plugin: Connection to (%s, %i) established.", ups->hostname,
265        ups->port);
266
267   // Output INFO or WARNING based on SSL
268   ssl_status = upscli_ssl(ups->conn); // 1 for SSL, 0 for not, -1 for error
269   if (ssl_status == 1) {
270     INFO("nut plugin: Connection is secured with SSL with no verification "
271          "of server SSL certificate.");
272   } else if (ssl_status == 0) {
273     WARNING("nut plugin: Connection is unsecured (no SSL).");
274   } else {
275     ERROR("nut plugin: nut_connect: upscli_ssl failed: %s",
276           upscli_strerror(ups->conn));
277     sfree(ups->conn);
278     return (-1);
279   } /* if (ssl_status == 1 && verify_peer == 1) */
280   return (0);
281 #endif
282 }
283
284 static int nut_read_one(nut_ups_t *ups) {
285   const char *query[3] = {"VAR", ups->upsname, NULL};
286   unsigned int query_num = 2;
287   char **answer;
288   unsigned int answer_num;
289   int status;
290
291   /* (Re-)Connect if we have no connection */
292   if (ups->conn == NULL) {
293     ups->conn = malloc(sizeof(*ups->conn));
294     if (ups->conn == NULL) {
295       ERROR("nut plugin: malloc failed.");
296       return (-1);
297     }
298
299     status = nut_connect(ups);
300     if (status == -1)
301       return -1;
302
303   } /* if (ups->conn == NULL) */
304
305   /* nut plugin: nut_read_one: upscli_list_start (adpos) failed: Protocol
306    * error */
307   status = upscli_list_start(ups->conn, query_num, query);
308   if (status != 0) {
309     ERROR("nut plugin: nut_read_one: upscli_list_start (%s) failed: %s",
310           ups->upsname, upscli_strerror(ups->conn));
311     upscli_disconnect(ups->conn);
312     sfree(ups->conn);
313 #ifdef WITH_UPSCLIENT_27
314     upscli_cleanup();
315 #endif
316     return (-1);
317   }
318
319   while ((status = upscli_list_next(ups->conn, query_num, query, &answer_num,
320                                     &answer)) == 1) {
321     char *key;
322     double value;
323
324     if (answer_num < 4)
325       continue;
326
327     key = answer[2];
328     value = atof(answer[3]);
329
330     if (strncmp("ambient.", key, 8) == 0) {
331       if (strcmp("ambient.humidity", key) == 0)
332         nut_submit(ups, "humidity", "ambient", value);
333       else if (strcmp("ambient.temperature", key) == 0)
334         nut_submit(ups, "temperature", "ambient", value);
335     } else if (strncmp("battery.", key, 8) == 0) {
336       if (strcmp("battery.charge", key) == 0)
337         nut_submit(ups, "percent", "charge", value);
338       else if (strcmp("battery.current", key) == 0)
339         nut_submit(ups, "current", "battery", value);
340       else if (strcmp("battery.runtime", key) == 0)
341         nut_submit(ups, "timeleft", "battery", value);
342       else if (strcmp("battery.temperature", key) == 0)
343         nut_submit(ups, "temperature", "battery", value);
344       else if (strcmp("battery.voltage", key) == 0)
345         nut_submit(ups, "voltage", "battery", value);
346     } else if (strncmp("input.", key, 6) == 0) {
347       if (strcmp("input.frequency", key) == 0)
348         nut_submit(ups, "frequency", "input", value);
349       else if (strcmp("input.voltage", key) == 0)
350         nut_submit(ups, "voltage", "input", value);
351     } else if (strncmp("output.", key, 7) == 0) {
352       if (strcmp("output.current", key) == 0)
353         nut_submit(ups, "current", "output", value);
354       else if (strcmp("output.frequency", key) == 0)
355         nut_submit(ups, "frequency", "output", value);
356       else if (strcmp("output.voltage", key) == 0)
357         nut_submit(ups, "voltage", "output", value);
358     } else if (strncmp("ups.", key, 4) == 0) {
359       if (strcmp("ups.load", key) == 0)
360         nut_submit(ups, "percent", "load", value);
361       else if (strcmp("ups.power", key) == 0)
362         nut_submit(ups, "power", "ups", value);
363       else if (strcmp("ups.temperature", key) == 0)
364         nut_submit(ups, "temperature", "ups", value);
365     }
366   } /* while (upscli_list_next) */
367
368   return (0);
369 } /* int nut_read_one */
370
371 static int nut_read(void) {
372   int success = 0;
373
374   pthread_mutex_lock(&read_lock);
375   success = read_busy;
376   read_busy = 1;
377   pthread_mutex_unlock(&read_lock);
378
379   if (success != 0)
380     return (0);
381
382   for (nut_ups_t *ups = upslist_head; ups != NULL; ups = ups->next)
383     if (nut_read_one(ups) == 0)
384       success++;
385
386   pthread_mutex_lock(&read_lock);
387   read_busy = 0;
388   pthread_mutex_unlock(&read_lock);
389
390   return ((success != 0) ? 0 : -1);
391 } /* int nut_read */
392
393 static int nut_shutdown(void) {
394   nut_ups_t *this;
395   nut_ups_t *next;
396
397   this = upslist_head;
398   while (this != NULL) {
399     next = this->next;
400     free_nut_ups_t(this);
401     this = next;
402   }
403 #ifdef WITH_UPSCLIENT_27
404   upscli_cleanup();
405 #endif
406
407   return (0);
408 } /* int nut_shutdown */
409
410 void module_register(void) {
411   plugin_register_config("nut", nut_config, config_keys, config_keys_num);
412   plugin_register_read("nut", nut_read);
413   plugin_register_shutdown("nut", nut_shutdown);
414 } /* void module_register */