1 /******************************************************************************
2 *
3 * CHECK_UPS.C
4 *
5 * Program: UPS monitor plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 *
9 * Last Modified: $Date$
10 *
11 * Command line: CHECK_UPS <host_address> [-u ups] [-p port] [-v variable] \
12 * [-wv warn_value] [-cv crit_value] [-to to_sec]
13 *
14 * Description:
15 *
17 * This plugin attempts to determine the status of an UPS
18 * (Uninterruptible Power Supply) on a remote host (or the local host)
19 * that is being monitored with Russel Kroll's "Smarty UPS Tools"
20 * package. If the UPS is online or calibrating, the plugin will
21 * return an OK state. If the battery is on it will return a WARNING
22 * state. If the UPS is off or has a low battery the plugin will
23 * return a CRITICAL state. You may also specify a variable to check
24 * (such as temperature, utility voltage, battery load, etc.) as well
25 * as warning and critical thresholds for the value of that variable.
26 * If the remote host has multiple UPS that are being monitored you
27 * will have to use the [ups] option to specify which UPS to check.
28 *
29 * Notes:
30 *
31 * This plugin requires that the UPSD daemon distributed with Russel
32 * Kroll's "Smart UPS Tools" be installed on the remote host. If you
33 * don't have the package installed on your system, you can download
34 * it from http://www.exploits.org/nut
35 *
36 * License Information:
37 *
38 * This program is free software; you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation; either version 2 of the License, or
41 * (at your option) any later version.
42 *
43 * This program is distributed in the hope that it will be useful,
44 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 * GNU General Public License for more details.
47 *
48 * You should have received a copy of the GNU General Public License
49 * along with this program; if not, write to the Free Software
50 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
51 *
52 ******************************************************************************/
54 #include "config.h"
55 #include "common.h"
56 #include "netutils.h"
57 #include "utils.h"
59 const char *progname = "check_ups";
60 #define REVISION "$Revision$"
61 #define COPYRIGHT "1999-2002"
62 #define AUTHOR "Ethan Galstad"
63 #define EMAIL "nagios@nagios.org"
65 #define CHECK_NONE 0
67 #define PORT 3493
69 #define UPS_NONE 0 /* no supported options */
70 #define UPS_UTILITY 1 /* supports utility line voltage */
71 #define UPS_BATTPCT 2 /* supports percent battery remaining */
72 #define UPS_STATUS 4 /* supports UPS status */
73 #define UPS_TEMP 8 /* supports UPS temperature */
74 #define UPS_LOADPCT 16 /* supports load percent */
76 #define UPSSTATUS_NONE 0
77 #define UPSSTATUS_OFF 1
78 #define UPSSTATUS_OL 2
79 #define UPSSTATUS_OB 4
80 #define UPSSTATUS_LB 8
81 #define UPSSTATUS_CAL 16
82 #define UPSSTATUS_RB 32 /*Replace Battery */
83 #define UPSSTATUS_UNKOWN 64
85 int server_port = PORT;
86 char *server_address = "127.0.0.1";
87 char *ups_name = NULL;
88 double warning_value = 0.0L;
89 double critical_value = 0.0L;
90 int check_warning_value = FALSE;
91 int check_critical_value = FALSE;
92 int check_variable = UPS_NONE;
93 int supported_options = UPS_NONE;
94 int status = UPSSTATUS_NONE;
96 double ups_utility_voltage = 0.0L;
97 double ups_battery_percent = 0.0L;
98 double ups_load_percent = 0.0L;
99 double ups_temperature = 0.0L;
100 char *ups_status = "N/A";
102 int determine_status (void);
103 int determine_supported_vars (void);
104 int get_ups_variable (const char *, char *, int);
106 int process_arguments (int, char **);
107 int validate_arguments (void);
108 void print_help (void);
109 void print_usage (void);
111 int
112 main (int argc, char **argv)
113 {
114 int result = STATE_OK;
115 char *message;
116 char temp_buffer[MAX_INPUT_BUFFER];
118 double ups_utility_deviation = 0.0L;
120 if (process_arguments (argc, argv) != OK)
121 usage ("Invalid command arguments supplied\n");
123 /* initialize alarm signal handling */
124 signal (SIGALRM, socket_timeout_alarm_handler);
126 /* set socket timeout */
127 alarm (socket_timeout);
129 /* determine what variables the UPS supports */
130 if (determine_supported_vars () != OK)
131 return STATE_CRITICAL;
133 /* get the ups status if possible */
134 if (supported_options & UPS_STATUS) {
136 if (determine_status () != OK)
137 return STATE_CRITICAL;
138 asprintf (&ups_status, "");
139 result = STATE_OK;
141 if (status & UPSSTATUS_OFF) {
142 asprintf (&ups_status, "Off");
143 result = STATE_CRITICAL;
144 }
145 else if ((status & (UPSSTATUS_OB | UPSSTATUS_LB)) ==
146 (UPSSTATUS_OB | UPSSTATUS_LB)) {
147 asprintf (&ups_status, "On Battery, Low Battery");
148 result = STATE_CRITICAL;
149 }
150 else {
151 if (status & UPSSTATUS_OL) {
152 asprintf (&ups_status, "%s%s", ups_status, "Online");
153 }
154 if (status & UPSSTATUS_OB) {
155 asprintf (&ups_status, "%s%s", ups_status, "On Battery");
156 result = STATE_WARNING;
157 }
158 if (status & UPSSTATUS_LB) {
159 asprintf (&ups_status, "%s%s", ups_status, ", Low Battery");
160 result = STATE_WARNING;
161 }
162 if (status & UPSSTATUS_CAL) {
163 asprintf (&ups_status, "%s%s", ups_status, ", Calibrating");
164 }
165 if (status & UPSSTATUS_RB) {
166 asprintf (&ups_status, "%s%s", ups_status, ", Replace Battery");
167 result = STATE_WARNING;
168 }
169 if (status & UPSSTATUS_UNKOWN) {
170 asprintf (&ups_status, "%s%s", ups_status, ", Unknown");
171 }
172 }
173 }
175 /* get the ups utility voltage if possible */
176 if (supported_options & UPS_UTILITY) {
178 if (get_ups_variable ("UTILITY", temp_buffer, sizeof (temp_buffer)) != OK)
179 return STATE_CRITICAL;
181 ups_utility_voltage = atof (temp_buffer);
183 if (ups_utility_voltage > 120.0)
184 ups_utility_deviation = 120.0 - ups_utility_voltage;
185 else
186 ups_utility_deviation = ups_utility_voltage - 120.0;
188 if (check_variable == UPS_UTILITY) {
189 if (check_critical_value == TRUE
190 && ups_utility_deviation >= critical_value) result = STATE_CRITICAL;
191 else if (check_warning_value == TRUE
192 && ups_utility_deviation >= warning_value
193 && result < STATE_WARNING) result = STATE_WARNING;
194 }
195 }
197 /* get the ups battery percent if possible */
198 if (supported_options & UPS_BATTPCT) {
200 if (get_ups_variable ("BATTPCT", temp_buffer, sizeof (temp_buffer)) != OK)
201 return STATE_CRITICAL;
203 ups_battery_percent = atof (temp_buffer);
205 if (check_variable == UPS_BATTPCT) {
206 if (check_critical_value == TRUE
207 && ups_battery_percent <= critical_value) result = STATE_CRITICAL;
208 else if (check_warning_value == TRUE
209 && ups_battery_percent <= warning_value
210 && result < STATE_WARNING) result = STATE_WARNING;
211 }
212 }
214 /* get the ups load percent if possible */
215 if (supported_options & UPS_LOADPCT) {
217 if (get_ups_variable ("LOADPCT", temp_buffer, sizeof (temp_buffer)) != OK)
218 return STATE_CRITICAL;
220 ups_load_percent = atof (temp_buffer);
222 if (check_variable == UPS_LOADPCT) {
223 if (check_critical_value == TRUE && ups_load_percent >= critical_value)
224 result = STATE_CRITICAL;
225 else if (check_warning_value == TRUE
226 && ups_load_percent >= warning_value && result < STATE_WARNING)
227 result = STATE_WARNING;
228 }
229 }
231 /* get the ups temperature if possible */
232 if (supported_options & UPS_TEMP) {
234 if (get_ups_variable ("UPSTEMP", temp_buffer, sizeof (temp_buffer)) != OK)
235 return STATE_CRITICAL;
237 ups_temperature = (atof (temp_buffer) * 1.8) + 32;
239 if (check_variable == UPS_TEMP) {
240 if (check_critical_value == TRUE && ups_temperature >= critical_value)
241 result = STATE_CRITICAL;
242 else if (check_warning_value == TRUE && ups_temperature >= warning_value
243 && result < STATE_WARNING)
244 result = STATE_WARNING;
245 }
246 }
248 /* if the UPS does not support any options we are looking for, report an error */
249 if (supported_options == UPS_NONE)
250 result = STATE_CRITICAL;
252 /* reset timeout */
253 alarm (0);
256 asprintf (&message, "UPS %s - ", (result == STATE_OK) ? "ok" : "problem");
258 if (supported_options & UPS_STATUS)
259 asprintf (&message, "%sStatus=%s ", message, ups_status);
261 if (supported_options & UPS_UTILITY)
262 asprintf (&message, "%sUtility=%3.1fV ", message, ups_utility_voltage);
264 if (supported_options & UPS_BATTPCT)
265 asprintf (&message, "%sBatt=%3.1f%% ", message, ups_battery_percent);
267 if (supported_options & UPS_LOADPCT)
268 asprintf (&message, "%sLoad=%3.1f%% ", message, ups_load_percent);
270 if (supported_options & UPS_TEMP)
271 asprintf (&message, "%sTemp=%3.1fF", message, ups_temperature);
273 if (supported_options == UPS_NONE)
274 asprintf (&message, "UPS does not support any available options\n");
276 printf ("%s\n", message);
278 return result;
279 }
283 /* determines what options are supported by the UPS */
284 int
285 determine_status (void)
286 {
287 char recv_buffer[MAX_INPUT_BUFFER];
288 char temp_buffer[MAX_INPUT_BUFFER];
289 char *ptr;
291 if (get_ups_variable ("STATUS", recv_buffer, sizeof (recv_buffer)) !=
292 STATE_OK) {
293 printf ("Invalid response received from hostn");
294 return ERROR;
295 }
297 recv_buffer[strlen (recv_buffer) - 1] = 0;
299 strcpy (temp_buffer, recv_buffer);
300 for (ptr = (char *) strtok (temp_buffer, " "); ptr != NULL;
301 ptr = (char *) strtok (NULL, " ")) {
302 if (!strcmp (ptr, "OFF"))
303 status |= UPSSTATUS_OFF;
304 else if (!strcmp (ptr, "OL"))
305 status |= UPSSTATUS_OL;
306 else if (!strcmp (ptr, "OB"))
307 status |= UPSSTATUS_OB;
308 else if (!strcmp (ptr, "LB"))
309 status |= UPSSTATUS_LB;
310 else if (!strcmp (ptr, "CAL"))
311 status |= UPSSTATUS_CAL;
312 else if (!strcmp (ptr, "RB"))
313 status |= UPSSTATUS_RB;
314 else
315 status |= UPSSTATUS_UNKOWN;
316 }
318 return OK;
319 }
322 /* determines what options are supported by the UPS */
323 int
324 determine_supported_vars (void)
325 {
326 char send_buffer[MAX_INPUT_BUFFER];
327 char recv_buffer[MAX_INPUT_BUFFER];
328 char temp_buffer[MAX_INPUT_BUFFER];
329 char *ptr;
332 /* get the list of variables that this UPS supports */
333 if (ups_name)
334 sprintf (send_buffer, "LISTVARS %s\r\n", ups_name);
335 else
336 sprintf (send_buffer, "LISTVARS\r\n");
337 if (process_tcp_request
338 (server_address, server_port, send_buffer, recv_buffer,
339 sizeof (recv_buffer)) != STATE_OK) {
340 printf ("Invalid response received from host\n");
341 return ERROR;
342 }
344 recv_buffer[strlen (recv_buffer) - 1] = 0;
346 if (ups_name)
347 ptr = recv_buffer + 5 + strlen (ups_name) + 2;
348 else
349 ptr = recv_buffer + 5;
351 strcpy (temp_buffer, recv_buffer);
353 for (ptr = (char *) strtok (temp_buffer, " "); ptr != NULL;
354 ptr = (char *) strtok (NULL, " ")) {
355 if (!strcmp (ptr, "UTILITY"))
356 supported_options |= UPS_UTILITY;
357 else if (!strcmp (ptr, "BATTPCT"))
358 supported_options |= UPS_BATTPCT;
359 else if (!strcmp (ptr, "LOADPCT"))
360 supported_options |= UPS_LOADPCT;
361 else if (!strcmp (ptr, "STATUS"))
362 supported_options |= UPS_STATUS;
363 else if (!strcmp (ptr, "UPSTEMP"))
364 supported_options |= UPS_TEMP;
365 }
367 return OK;
368 }
371 /* gets a variable value for a specific UPS */
372 int
373 get_ups_variable (const char *varname, char *buf, int buflen)
374 {
375 /* char command[MAX_INPUT_BUFFER]; */
376 char temp_buffer[MAX_INPUT_BUFFER];
377 char send_buffer[MAX_INPUT_BUFFER];
378 char *ptr;
380 /* create the command string to send to the UPS daemon */
381 if (ups_name)
382 sprintf (send_buffer, "REQ %s@%s\n", varname, ups_name);
383 else
384 sprintf (send_buffer, "REQ %s\n", varname);
386 /* send the command to the daemon and get a response back */
387 if (process_tcp_request
388 (server_address, server_port, send_buffer, temp_buffer,
389 sizeof (temp_buffer)) != STATE_OK) {
390 printf ("Invalid response received from host\n");
391 return ERROR;
392 }
394 if (ups_name)
395 ptr = temp_buffer + strlen (varname) + 5 + strlen (ups_name) + 1;
396 else
397 ptr = temp_buffer + strlen (varname) + 5;
399 if (!strcmp (ptr, "NOT-SUPPORTED")) {
400 printf ("Error: Variable '%s' is not supported\n", varname);
401 return ERROR;
402 }
404 if (!strcmp (ptr, "DATA-STALE")) {
405 printf ("Error: UPS data is stale\n");
406 return ERROR;
407 }
409 if (!strcmp (ptr, "UNKNOWN-UPS")) {
410 if (ups_name)
411 printf ("Error: UPS '%s' is unknown\n", ups_name);
412 else
413 printf ("Error: UPS is unknown\n");
414 return ERROR;
415 }
417 strncpy (buf, ptr, buflen - 1);
418 buf[buflen - 1] = 0;
420 return OK;
421 }
427 /* Command line: CHECK_UPS <host_address> [-u ups] [-p port] [-v variable]
428 [-wv warn_value] [-cv crit_value] [-to to_sec] */
431 /* process command-line arguments */
432 int
433 process_arguments (int argc, char **argv)
434 {
435 int c;
437 int option_index = 0;
438 static struct option long_options[] = {
439 {"hostname", required_argument, 0, 'H'},
440 {"ups", required_argument, 0, 'u'},
441 {"port", required_argument, 0, 'p'},
442 {"critical", required_argument, 0, 'c'},
443 {"warning", required_argument, 0, 'w'},
444 {"timeout", required_argument, 0, 't'},
445 {"variable", required_argument, 0, 'v'},
446 {"version", no_argument, 0, 'V'},
447 {"help", no_argument, 0, 'h'},
448 {0, 0, 0, 0}
449 };
451 if (argc < 2)
452 return ERROR;
454 for (c = 1; c < argc; c++) {
455 if (strcmp ("-to", argv[c]) == 0)
456 strcpy (argv[c], "-t");
457 else if (strcmp ("-wt", argv[c]) == 0)
458 strcpy (argv[c], "-w");
459 else if (strcmp ("-ct", argv[c]) == 0)
460 strcpy (argv[c], "-c");
461 }
463 while (1) {
464 c = getopt_long (argc, argv, "hVH:u:p:v:c:w:t:", long_options,
465 &option_index);
467 if (c == -1 || c == EOF)
468 break;
470 switch (c) {
471 case '?': /* help */
472 usage3 ("Unknown option", optopt);
473 case 'H': /* hostname */
474 if (is_host (optarg)) {
475 server_address = optarg;
476 }
477 else {
478 usage2 ("Invalid host name", optarg);
479 }
480 break;
481 case 'u': /* ups name */
482 ups_name = optarg;
483 break;
484 case 'p': /* port */
485 if (is_intpos (optarg)) {
486 server_port = atoi (optarg);
487 }
488 else {
489 usage2 ("Server port must be a positive integer", optarg);
490 }
491 break;
492 case 'c': /* critical time threshold */
493 if (is_intnonneg (optarg)) {
494 critical_value = atoi (optarg);
495 check_critical_value = TRUE;
496 }
497 else {
498 usage2 ("Critical time must be a nonnegative integer", optarg);
499 }
500 break;
501 case 'w': /* warning time threshold */
502 if (is_intnonneg (optarg)) {
503 warning_value = atoi (optarg);
504 check_warning_value = TRUE;
505 }
506 else {
507 usage2 ("Warning time must be a nonnegative integer", optarg);
508 }
509 break;
510 case 'v': /* variable */
511 if (!strcmp (optarg, "LINE"))
512 check_variable = UPS_UTILITY;
513 else if (!strcmp (optarg, "TEMP"))
514 check_variable = UPS_TEMP;
515 else if (!strcmp (optarg, "BATTPCT"))
516 check_variable = UPS_BATTPCT;
517 else if (!strcmp (optarg, "LOADPCT"))
518 check_variable = UPS_LOADPCT;
519 else
520 usage2 ("Unrecognized UPS variable", optarg);
521 break;
522 case 't': /* timeout */
523 if (is_intnonneg (optarg)) {
524 socket_timeout = atoi (optarg);
525 }
526 else {
527 usage ("Time interval must be a nonnegative integer\n");
528 }
529 break;
530 case 'V': /* version */
531 print_revision (progname, "$Revision$");
532 exit (STATE_OK);
533 case 'h': /* help */
534 print_help ();
535 exit (STATE_OK);
536 }
537 }
540 if (server_address == NULL && argc > optind) {
541 if (is_host (argv[optind]))
542 server_address = argv[optind++];
543 else
544 usage ("Invalid host name");
545 }
547 return validate_arguments();
548 }
554 int
555 validate_arguments (void)
556 {
557 return OK;
558 }
564 void
565 print_help (void)
566 {
567 print_revision (progname, "$Revision$");
568 printf
569 ("Copyright (c) 2000 Tom Shields/Karl DeBisschop\n\n"
570 "This plugin tests the UPS service on the specified host.\n"
571 "Newtork UPS Tools for www.exploits.org must be running for this plugin to work.\n\n");
572 print_usage ();
573 printf
574 ("\nOptions:\n"
575 " -H, --hostname=STRING or IPADDRESS\n"
576 " Check server on the indicated host\n"
577 " -p, --port=INTEGER\n"
578 " Make connection on the indicated port (default: %d)\n"
579 " -u, --ups=STRING\n"
580 " Name of UPS\n"
581 " -w, --warning=INTEGER\n"
582 " Seconds necessary to result in a warning status\n"
583 " -c, --critical=INTEGER\n"
584 " Seconds necessary to result in a critical status\n"
585 " -t, --timeout=INTEGER\n"
586 " Seconds before connection attempt times out (default: %d)\n"
587 " -v, --verbose\n"
588 " Print extra information (command-line use only)\n"
589 " -h, --help\n"
590 " Print detailed help screen\n"
591 " -V, --version\n"
592 " Print version information\n\n", PORT, DEFAULT_SOCKET_TIMEOUT);
593 support ();
594 }
600 void
601 print_usage (void)
602 {
603 printf
604 ("Usage: %s -H host [-e expect] [-p port] [-w warn] [-c crit]\n"
605 " [-t timeout] [-v]\n"
606 " %s --help\n"
607 " %s --version\n", progname, progname, progname);
608 }