3e538bca036ddba67dca88b22f2b8c31ca9cde82
1 /******************************************************************************
3 check_ups
5 Program: Network UPS Tools plugin for Nagios
6 License: GPL
7 Copyright (c) 2000 Tom Shields
8 2004 Alain Richard <alain.richard@equation.fr>
9 2004 Arnaud Quette <arnaud.quette@mgeups.com>
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or (at
15 your option) any later version.
17 This program is distributed in the hope that it will be useful, but
18 WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 $Id$
28 ******************************************************************************/
30 const char *progname = "check_ups";
31 const char *revision = "$Revision$";
32 const char *copyright = "2000-2004";
33 const char *email = "nagiosplug-devel@lists.sourceforge.net";
35 #include <locale.h>
36 #include "common.h"
37 #include "netutils.h"
38 #include "utils.h"
40 enum {
41 PORT = 3493
42 };
44 #define CHECK_NONE 0
46 #define UPS_NONE 0 /* no supported options */
47 #define UPS_UTILITY 1 /* supports utility line voltage */
48 #define UPS_BATTPCT 2 /* supports percent battery remaining */
49 #define UPS_STATUS 4 /* supports UPS status */
50 #define UPS_TEMP 8 /* supports UPS temperature */
51 #define UPS_LOADPCT 16 /* supports load percent */
53 #define UPSSTATUS_NONE 0
54 #define UPSSTATUS_OFF 1
55 #define UPSSTATUS_OL 2
56 #define UPSSTATUS_OB 4
57 #define UPSSTATUS_LB 8
58 #define UPSSTATUS_CAL 16
59 #define UPSSTATUS_RB 32 /*Replace Battery */
60 #define UPSSTATUS_BYPASS 64
61 #define UPSSTATUS_OVER 128
62 #define UPSSTATUS_TRIM 256
63 #define UPSSTATUS_BOOST 512
64 #define UPSSTATUS_CHRG 1024
65 #define UPSSTATUS_DISCHRG 2048
66 #define UPSSTATUS_UNKOWN 4096
68 enum { NOSUCHVAR = ERROR-1 };
70 int server_port = PORT;
71 char *server_address;
72 char *ups_name = NULL;
73 double warning_value = 0.0;
74 double critical_value = 0.0;
75 int check_warn = FALSE;
76 int check_crit = FALSE;
77 int check_variable = UPS_NONE;
78 int supported_options = UPS_NONE;
79 int status = UPSSTATUS_NONE;
81 double ups_utility_voltage = 0.0;
82 double ups_battery_percent = 0.0;
83 double ups_load_percent = 0.0;
84 double ups_temperature = 0.0;
85 char *ups_status;
86 int temp_output_c = 0;
88 int determine_status (void);
89 int get_ups_variable (const char *, char *, size_t);
91 int process_arguments (int, char **);
92 int validate_arguments (void);
93 void print_help (void);
94 void print_usage (void);
96 int
97 main (int argc, char **argv)
98 {
99 int result = STATE_UNKNOWN;
100 char *message;
101 char *data;
102 char temp_buffer[MAX_INPUT_BUFFER];
103 double ups_utility_deviation = 0.0;
104 int res;
106 setlocale (LC_ALL, "");
107 bindtextdomain (PACKAGE, LOCALEDIR);
108 textdomain (PACKAGE);
110 ups_status = strdup ("N/A");
111 data = strdup ("");
112 message = strdup ("");
114 if (process_arguments (argc, argv) != TRUE)
115 usage4 (_("Could not parse arguments"));
117 /* initialize alarm signal handling */
118 signal (SIGALRM, socket_timeout_alarm_handler);
120 /* set socket timeout */
121 alarm (socket_timeout);
123 /* get the ups status if possible */
124 if (determine_status () != OK)
125 return STATE_CRITICAL;
126 if (supported_options & UPS_STATUS) {
128 ups_status = strdup ("");
129 result = STATE_OK;
131 if (status & UPSSTATUS_OFF) {
132 asprintf (&ups_status, "Off");
133 result = STATE_CRITICAL;
134 }
135 else if ((status & (UPSSTATUS_OB | UPSSTATUS_LB)) ==
136 (UPSSTATUS_OB | UPSSTATUS_LB)) {
137 asprintf (&ups_status, "On Battery, Low Battery");
138 result = STATE_CRITICAL;
139 }
140 else {
141 if (status & UPSSTATUS_OL) {
142 asprintf (&ups_status, "%s%s", ups_status, "Online");
143 }
144 if (status & UPSSTATUS_OB) {
145 asprintf (&ups_status, "%s%s", ups_status, "On Battery");
146 result = STATE_WARNING;
147 }
148 if (status & UPSSTATUS_LB) {
149 asprintf (&ups_status, "%s%s", ups_status, ", Low Battery");
150 result = STATE_WARNING;
151 }
152 if (status & UPSSTATUS_CAL) {
153 asprintf (&ups_status, "%s%s", ups_status, ", Calibrating");
154 }
155 if (status & UPSSTATUS_RB) {
156 asprintf (&ups_status, "%s%s", ups_status, ", Replace Battery");
157 result = STATE_WARNING;
158 }
159 if (status & UPSSTATUS_BYPASS) {
160 asprintf (&ups_status, "%s%s", ups_status, ", On Bypass");
161 }
162 if (status & UPSSTATUS_OVER) {
163 asprintf (&ups_status, "%s%s", ups_status, ", Overload");
164 }
165 if (status & UPSSTATUS_TRIM) {
166 asprintf (&ups_status, "%s%s", ups_status, ", Trimming");
167 }
168 if (status & UPSSTATUS_BOOST) {
169 asprintf (&ups_status, "%s%s", ups_status, ", Boosting");
170 }
171 if (status & UPSSTATUS_CHRG) {
172 asprintf (&ups_status, "%s%s", ups_status, ", Charging");
173 }
174 if (status & UPSSTATUS_DISCHRG) {
175 asprintf (&ups_status, "%s%s", ups_status, ", Discharging");
176 }
177 if (status & UPSSTATUS_UNKOWN) {
178 asprintf (&ups_status, "%s%s", ups_status, ", Unknown");
179 }
180 }
181 asprintf (&message, "%sStatus=%s ", message, ups_status);
182 }
184 /* get the ups utility voltage if possible */
185 res=get_ups_variable ("input.voltage", temp_buffer, sizeof (temp_buffer));
186 if (res == NOSUCHVAR) supported_options &= ~UPS_UTILITY;
187 else if (res != OK)
188 return STATE_CRITICAL;
189 else {
190 supported_options |= UPS_UTILITY;
192 ups_utility_voltage = atof (temp_buffer);
193 asprintf (&message, "%sUtility=%3.1fV ", message, ups_utility_voltage);
195 if (ups_utility_voltage > 120.0)
196 ups_utility_deviation = 120.0 - ups_utility_voltage;
197 else
198 ups_utility_deviation = ups_utility_voltage - 120.0;
200 if (check_variable == UPS_UTILITY) {
201 if (check_crit==TRUE && ups_utility_deviation>=critical_value) {
202 result = STATE_CRITICAL;
203 }
204 else if (check_warn==TRUE && ups_utility_deviation>=warning_value) {
205 result = max_state (result, STATE_WARNING);
206 }
207 asprintf (&data, "%s",
208 perfdata ("voltage", (long)(1000*ups_utility_voltage), "mV",
209 check_warn, (long)(1000*warning_value),
210 check_crit, (long)(1000*critical_value),
211 TRUE, 0, FALSE, 0));
212 } else {
213 asprintf (&data, "%s",
214 perfdata ("voltage", (long)(1000*ups_utility_voltage), "mV",
215 FALSE, 0, FALSE, 0, TRUE, 0, FALSE, 0));
216 }
217 }
219 /* get the ups battery percent if possible */
220 res=get_ups_variable ("battery.charge", temp_buffer, sizeof (temp_buffer));
221 if (res == NOSUCHVAR) supported_options &= ~UPS_BATTPCT;
222 else if ( res != OK)
223 return STATE_CRITICAL;
224 else {
225 supported_options |= UPS_BATTPCT;
226 ups_battery_percent = atof (temp_buffer);
227 asprintf (&message, "%sBatt=%3.1f%% ", message, ups_battery_percent);
229 if (check_variable == UPS_BATTPCT) {
230 if (check_crit==TRUE && ups_battery_percent <= critical_value) {
231 result = STATE_CRITICAL;
232 }
233 else if (check_warn==TRUE && ups_battery_percent<=warning_value) {
234 result = max_state (result, STATE_WARNING);
235 }
236 asprintf (&data, "%s %s", data,
237 perfdata ("battery", (long)ups_battery_percent, "%",
238 check_warn, (long)(1000*warning_value),
239 check_crit, (long)(1000*critical_value),
240 TRUE, 0, TRUE, 100));
241 } else {
242 asprintf (&data, "%s %s", data,
243 perfdata ("battery", (long)ups_battery_percent, "%",
244 FALSE, 0, FALSE, 0, TRUE, 0, TRUE, 100));
245 }
246 }
248 /* get the ups load percent if possible */
249 res=get_ups_variable ("ups.load", temp_buffer, sizeof (temp_buffer));
250 if ( res == NOSUCHVAR ) supported_options &= ~UPS_LOADPCT;
251 else if ( res != OK)
252 return STATE_CRITICAL;
253 else {
254 supported_options |= UPS_LOADPCT;
255 ups_load_percent = atof (temp_buffer);
256 asprintf (&message, "%sLoad=%3.1f%% ", message, ups_load_percent);
258 if (check_variable == UPS_LOADPCT) {
259 if (check_crit==TRUE && ups_load_percent>=critical_value) {
260 result = STATE_CRITICAL;
261 }
262 else if (check_warn==TRUE && ups_load_percent>=warning_value) {
263 result = max_state (result, STATE_WARNING);
264 }
265 asprintf (&data, "%s %s", data,
266 perfdata ("load", (long)ups_load_percent, "%",
267 check_warn, (long)(1000*warning_value),
268 check_crit, (long)(1000*critical_value),
269 TRUE, 0, TRUE, 100));
270 } else {
271 asprintf (&data, "%s %s", data,
272 perfdata ("load", (long)ups_load_percent, "%",
273 FALSE, 0, FALSE, 0, TRUE, 0, TRUE, 100));
274 }
275 }
277 /* get the ups temperature if possible */
278 res=get_ups_variable ("ups.temperature", temp_buffer, sizeof (temp_buffer));
279 if ( res == NOSUCHVAR ) supported_options &= ~UPS_TEMP;
280 else if ( res != OK)
281 return STATE_CRITICAL;
282 else {
283 supported_options |= UPS_TEMP;
284 if (temp_output_c) {
285 ups_temperature = atof (temp_buffer);
286 asprintf (&message, "%sTemp=%3.1fC", message, ups_temperature);
287 }
288 else {
289 ups_temperature = (atof (temp_buffer) * 1.8) + 32;
290 asprintf (&message, "%sTemp=%3.1fF", message, ups_temperature);
291 }
293 if (check_variable == UPS_TEMP) {
294 if (check_crit==TRUE && ups_temperature>=critical_value) {
295 result = STATE_CRITICAL;
296 }
297 else if (check_warn == TRUE && ups_temperature>=warning_value) {
298 result = max_state (result, STATE_WARNING);
299 }
300 asprintf (&data, "%s %s", data,
301 perfdata ("temp", (long)ups_temperature, "degF",
302 check_warn, (long)(1000*warning_value),
303 check_crit, (long)(1000*critical_value),
304 TRUE, 0, FALSE, 0));
305 } else {
306 asprintf (&data, "%s %s", data,
307 perfdata ("temp", (long)ups_temperature, "degF",
308 FALSE, 0, FALSE, 0, TRUE, 0, FALSE, 0));
309 }
310 }
312 /* if the UPS does not support any options we are looking for, report an error */
313 if (supported_options == UPS_NONE) {
314 result = STATE_CRITICAL;
315 asprintf (&message, "UPS does not support any available options\n");
316 }
318 /* reset timeout */
319 alarm (0);
321 printf ("UPS %s - %s|%s\n", state_text(result), message, data);
322 return result;
323 }
327 /* determines what options are supported by the UPS */
328 int
329 determine_status (void)
330 {
331 char recv_buffer[MAX_INPUT_BUFFER];
332 char temp_buffer[MAX_INPUT_BUFFER];
333 char *ptr;
334 int res;
336 res=get_ups_variable ("ups.status", recv_buffer, sizeof (recv_buffer));
337 if (res == NOSUCHVAR) return OK;
338 if (res != STATE_OK) {
339 printf ("Invalid response received from host\n");
340 return ERROR;
341 }
343 supported_options |= UPS_STATUS;
345 strcpy (temp_buffer, recv_buffer);
346 for (ptr = (char *) strtok (temp_buffer, " "); ptr != NULL;
347 ptr = (char *) strtok (NULL, " ")) {
348 if (!strcmp (ptr, "OFF"))
349 status |= UPSSTATUS_OFF;
350 else if (!strcmp (ptr, "OL"))
351 status |= UPSSTATUS_OL;
352 else if (!strcmp (ptr, "OB"))
353 status |= UPSSTATUS_OB;
354 else if (!strcmp (ptr, "LB"))
355 status |= UPSSTATUS_LB;
356 else if (!strcmp (ptr, "CAL"))
357 status |= UPSSTATUS_CAL;
358 else if (!strcmp (ptr, "RB"))
359 status |= UPSSTATUS_RB;
360 else if (!strcmp (ptr, "BYPASS"))
361 status |= UPSSTATUS_BYPASS;
362 else if (!strcmp (ptr, "OVER"))
363 status |= UPSSTATUS_OVER;
364 else if (!strcmp (ptr, "TRIM"))
365 status |= UPSSTATUS_TRIM;
366 else if (!strcmp (ptr, "BOOST"))
367 status |= UPSSTATUS_BOOST;
368 else if (!strcmp (ptr, "CHRG"))
369 status |= UPSSTATUS_CHRG;
370 else if (!strcmp (ptr, "DISCHRG"))
371 status |= UPSSTATUS_DISCHRG;
372 else
373 status |= UPSSTATUS_UNKOWN;
374 }
376 return OK;
377 }
380 /* gets a variable value for a specific UPS */
381 int
382 get_ups_variable (const char *varname, char *buf, size_t buflen)
383 {
384 /* char command[MAX_INPUT_BUFFER]; */
385 char temp_buffer[MAX_INPUT_BUFFER];
386 char send_buffer[MAX_INPUT_BUFFER];
387 char *ptr;
388 int len;
390 *buf=0;
392 /* create the command string to send to the UPS daemon */
393 sprintf (send_buffer, "GET VAR %s %s\n", ups_name, varname);
395 /* send the command to the daemon and get a response back */
396 if (process_tcp_request
397 (server_address, server_port, send_buffer, temp_buffer,
398 sizeof (temp_buffer)) != STATE_OK) {
399 printf ("Invalid response received from host\n");
400 return ERROR;
401 }
403 ptr = temp_buffer;
404 len = strlen(ptr);
405 if (len > 0 && ptr[len-1] == '\n') ptr[len-1]=0;
406 if (strcmp (ptr, "ERR UNKNOWN-UPS") == 0) {
407 printf ("CRITICAL - no such ups '%s' on that host\n", ups_name);
408 return ERROR;
409 }
411 if (strcmp (ptr, "ERR VAR-NOT-SUPPORTED") == 0) {
412 //printf ("Error: Variable '%s' is not supported\n", varname);
413 return NOSUCHVAR;
414 }
416 if (strcmp (ptr, "ERR DATA-STALE") == 0) {
417 printf ("CRITICAL - UPS data is stale\n");
418 return ERROR;
419 }
421 if (strncmp (ptr, "ERR", 3) == 0) {
422 printf ("Unknown error: %s\n", ptr);
423 return ERROR;
424 }
426 ptr = temp_buffer + strlen (varname) + strlen (ups_name) + 6;
427 len = strlen(ptr);
428 if (len < 2 || ptr[0] != '"' || ptr[len-1] != '"') {
429 printf ("Error: unable to parse variable\n");
430 return ERROR;
431 }
432 strncpy (buf, ptr+1, len - 2);
433 buf[len - 2] = 0;
435 return OK;
436 }
439 /* Command line: CHECK_UPS -H <host_address> -u ups [-p port] [-v variable]
440 [-wv warn_value] [-cv crit_value] [-to to_sec] */
443 /* process command-line arguments */
444 int
445 process_arguments (int argc, char **argv)
446 {
447 int c;
449 int option = 0;
450 static struct option longopts[] = {
451 {"hostname", required_argument, 0, 'H'},
452 {"ups", required_argument, 0, 'u'},
453 {"port", required_argument, 0, 'p'},
454 {"critical", required_argument, 0, 'c'},
455 {"warning", required_argument, 0, 'w'},
456 {"timeout", required_argument, 0, 't'},
457 {"temperature", no_argument, 0, 'T'},
458 {"variable", required_argument, 0, 'v'},
459 {"version", no_argument, 0, 'V'},
460 {"help", no_argument, 0, 'h'},
461 {0, 0, 0, 0}
462 };
464 if (argc < 2)
465 return ERROR;
467 for (c = 1; c < argc; c++) {
468 if (strcmp ("-to", argv[c]) == 0)
469 strcpy (argv[c], "-t");
470 else if (strcmp ("-wt", argv[c]) == 0)
471 strcpy (argv[c], "-w");
472 else if (strcmp ("-ct", argv[c]) == 0)
473 strcpy (argv[c], "-c");
474 }
476 while (1) {
477 c = getopt_long (argc, argv, "hVTH:u:p:v:c:w:t:", longopts,
478 &option);
480 if (c == -1 || c == EOF)
481 break;
483 switch (c) {
484 case '?': /* help */
485 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
486 print_usage ();
487 exit (STATE_UNKNOWN);
488 case 'H': /* hostname */
489 if (is_host (optarg)) {
490 server_address = optarg;
491 }
492 else {
493 usage2 (_("Invalid hostname/address"), optarg);
494 }
495 break;
496 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for Farenheit) */
497 temp_output_c = 1;
498 break;
499 case 'u': /* ups name */
500 ups_name = optarg;
501 break;
502 case 'p': /* port */
503 if (is_intpos (optarg)) {
504 server_port = atoi (optarg);
505 }
506 else {
507 usage2 ("Port must be a positive integer", optarg);
508 }
509 break;
510 case 'c': /* critical time threshold */
511 if (is_intnonneg (optarg)) {
512 critical_value = atoi (optarg);
513 check_crit = TRUE;
514 }
515 else {
516 usage2 ("Critical time must be a positive integer", optarg);
517 }
518 break;
519 case 'w': /* warning time threshold */
520 if (is_intnonneg (optarg)) {
521 warning_value = atoi (optarg);
522 check_warn = TRUE;
523 }
524 else {
525 usage2 ("Warning time must be a positive integer", optarg);
526 }
527 break;
528 case 'v': /* variable */
529 if (!strcmp (optarg, "LINE"))
530 check_variable = UPS_UTILITY;
531 else if (!strcmp (optarg, "TEMP"))
532 check_variable = UPS_TEMP;
533 else if (!strcmp (optarg, "BATTPCT"))
534 check_variable = UPS_BATTPCT;
535 else if (!strcmp (optarg, "LOADPCT"))
536 check_variable = UPS_LOADPCT;
537 else
538 usage2 ("Unrecognized UPS variable", optarg);
539 break;
540 case 't': /* timeout */
541 if (is_intnonneg (optarg)) {
542 socket_timeout = atoi (optarg);
543 }
544 else {
545 usage ("Time interval must be a positive integer\n");
546 }
547 break;
548 case 'V': /* version */
549 print_revision (progname, revision);
550 exit (STATE_OK);
551 case 'h': /* help */
552 print_help ();
553 exit (STATE_OK);
554 }
555 }
558 if (server_address == NULL && argc > optind) {
559 if (is_host (argv[optind]))
560 server_address = argv[optind++];
561 else
562 usage2 (_("Invalid hostname/address"), optarg);
563 }
565 if (server_address == NULL)
566 server_address = strdup("127.0.0.1");
568 return validate_arguments();
569 }
573 int
574 validate_arguments (void)
575 {
576 if (! ups_name) {
577 printf ("Error : no ups indicated\n");
578 return ERROR;
579 }
580 return OK;
581 }
585 void
586 print_help (void)
587 {
588 char *myport;
589 asprintf (&myport, "%d", PORT);
591 print_revision (progname, revision);
593 printf ("Copyright (c) 2000 Tom Shields");
594 printf ("Copyright (c) 2004 Alain Richard <alain.richard@equation.fr>\n");
595 printf ("Copyright (c) 2004 Arnaud Quette <arnaud.quette@mgeups.com>\n");
597 printf (COPYRIGHT, copyright, email);
599 printf (_("This plugin tests the UPS service on the specified host.\n\
600 Network UPS Tools from www.networkupstools.org must be running for this\n\
601 plugin to work.\n\n"));
603 print_usage ();
605 printf (_(UT_HELP_VRSN));
607 printf (_(UT_HOST_PORT), 'p', myport);
609 printf (_("\
610 -u, --ups=STRING\n\
611 Name of UPS\n"));
613 printf (_("\
614 -T, --temperature\n\
615 Output of temperatures in Celsius\n"));
617 printf (_(UT_WARN_CRIT));
619 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
621 printf (_(UT_VERBOSE));
623 printf (_("\
624 This plugin attempts to determine the status of a UPS (Uninterruptible Power\n\
625 Supply) on a local or remote host. If the UPS is online or calibrating, the\n\
626 plugin will return an OK state. If the battery is on it will return a WARNING\n\
627 state. If the UPS is off or has a low battery the plugin will return a CRITICAL\n\
628 state.\n\n"));
630 printf (_("\
631 You may also specify a variable to check [such as temperature, utility voltage,\n\
632 battery load, etc.] as well as warning and critical thresholds for the value of\n\
633 that variable. If the remote host has multiple UPS that are being monitored you\n\
634 will have to use the [ups] option to specify which UPS to check.\n\n"));
636 printf (_("Notes:\n\n\
637 This plugin requires that the UPSD daemon distributed with Russel Kroll's\n\
638 Smart UPS Tools be installed on the remote host. If you do not have the\n\
639 package installed on your system, you can download it from\n\
640 http://www.networkupstools.org\n\n"));
642 printf (_(UT_SUPPORT));
643 }
647 void
648 print_usage (void)
649 {
650 printf (_("\
651 Usage: %s -H host -u ups [-p port] [-v variable]\n\
652 [-wv warn_value] [-cv crit_value] [-to to_sec] [-T]\n"), progname);
653 printf (_(UT_HLP_VRS), progname, progname);
654 }