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 usage2 (_("Unknown argument"), optarg);
485 case 'H': /* hostname */
486 if (is_host (optarg)) {
487 server_address = optarg;
488 }
489 else {
490 usage2 (_("Invalid hostname/address"), optarg);
491 }
492 break;
493 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for Farenheit) */
494 temp_output_c = 1;
495 break;
496 case 'u': /* ups name */
497 ups_name = optarg;
498 break;
499 case 'p': /* port */
500 if (is_intpos (optarg)) {
501 server_port = atoi (optarg);
502 }
503 else {
504 usage2 (_("Port must be a positive integer"), optarg);
505 }
506 break;
507 case 'c': /* critical time threshold */
508 if (is_intnonneg (optarg)) {
509 critical_value = atoi (optarg);
510 check_crit = TRUE;
511 }
512 else {
513 usage2 (_("Critical time must be a positive integer"), optarg);
514 }
515 break;
516 case 'w': /* warning time threshold */
517 if (is_intnonneg (optarg)) {
518 warning_value = atoi (optarg);
519 check_warn = TRUE;
520 }
521 else {
522 usage2 (_("Warning time must be a positive integer"), optarg);
523 }
524 break;
525 case 'v': /* variable */
526 if (!strcmp (optarg, "LINE"))
527 check_variable = UPS_UTILITY;
528 else if (!strcmp (optarg, "TEMP"))
529 check_variable = UPS_TEMP;
530 else if (!strcmp (optarg, "BATTPCT"))
531 check_variable = UPS_BATTPCT;
532 else if (!strcmp (optarg, "LOADPCT"))
533 check_variable = UPS_LOADPCT;
534 else
535 usage2 (_("Unrecognized UPS variable"), optarg);
536 break;
537 case 't': /* timeout */
538 if (is_intnonneg (optarg)) {
539 socket_timeout = atoi (optarg);
540 }
541 else {
542 usage4 (_("Timeout interval must be a positive integer"));
543 }
544 break;
545 case 'V': /* version */
546 print_revision (progname, revision);
547 exit (STATE_OK);
548 case 'h': /* help */
549 print_help ();
550 exit (STATE_OK);
551 }
552 }
555 if (server_address == NULL && argc > optind) {
556 if (is_host (argv[optind]))
557 server_address = argv[optind++];
558 else
559 usage2 (_("Invalid hostname/address"), optarg);
560 }
562 if (server_address == NULL)
563 server_address = strdup("127.0.0.1");
565 return validate_arguments();
566 }
569 int
570 validate_arguments (void)
571 {
572 if (! ups_name) {
573 printf (_("Error : no ups indicated\n"));
574 return ERROR;
575 }
576 return OK;
577 }
580 void
581 print_help (void)
582 {
583 char *myport;
584 asprintf (&myport, "%d", PORT);
586 print_revision (progname, revision);
588 printf ("Copyright (c) 2000 Tom Shields");
589 printf ("Copyright (c) 2004 Alain Richard <alain.richard@equation.fr>\n");
590 printf ("Copyright (c) 2004 Arnaud Quette <arnaud.quette@mgeups.com>\n");
591 printf (COPYRIGHT, copyright, email);
593 printf (_("This plugin tests the UPS service on the specified host.\n\
594 Network UPS Tools from www.networkupstools.org must be running for this\n\
595 plugin to work.\n\n"));
597 print_usage ();
599 printf (_(UT_HELP_VRSN));
601 printf (_(UT_HOST_PORT), 'p', myport);
603 printf (_("\
604 -u, --ups=STRING\n\
605 Name of UPS\n"));
607 printf (_("\
608 -T, --temperature\n\
609 Output of temperatures in Celsius\n"));
611 printf (_(UT_WARN_CRIT));
613 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
615 printf (_(UT_VERBOSE));
617 printf (_("\
618 This plugin attempts to determine the status of a UPS (Uninterruptible Power\n\
619 Supply) on a local or remote host. If the UPS is online or calibrating, the\n\
620 plugin will return an OK state. If the battery is on it will return a WARNING\n\
621 state. If the UPS is off or has a low battery the plugin will return a CRITICAL\n\
622 state.\n\n"));
624 printf (_("\
625 You may also specify a variable to check [such as temperature, utility voltage,\n\
626 battery load, etc.] as well as warning and critical thresholds for the value of\n\
627 that variable. If the remote host has multiple UPS that are being monitored you\n\
628 will have to use the [ups] option to specify which UPS to check.\n\n"));
630 printf (_("Notes:\n\n\
631 This plugin requires that the UPSD daemon distributed with Russel Kroll's\n\
632 Smart UPS Tools be installed on the remote host. If you do not have the\n\
633 package installed on your system, you can download it from\n\
634 http://www.networkupstools.org\n\n"));
636 printf (_(UT_SUPPORT));
637 }
640 void
641 print_usage (void)
642 {
643 printf ("\
644 Usage: %s -H host -u ups [-p port] [-v variable]\n\
645 [-wv warn_value] [-cv crit_value] [-to to_sec] [-T]\n", progname);
646 }