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