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 *tunits;
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) == ERROR)
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 tunits="degC";
286 ups_temperature = atof (temp_buffer);
287 asprintf (&message, "%sTemp=%3.1fC", message, ups_temperature);
288 }
289 else {
290 tunits="degF";
291 ups_temperature = (atof (temp_buffer) * 1.8) + 32;
292 asprintf (&message, "%sTemp=%3.1fF", message, ups_temperature);
293 }
295 if (check_variable == UPS_TEMP) {
296 if (check_crit==TRUE && ups_temperature>=critical_value) {
297 result = STATE_CRITICAL;
298 }
299 else if (check_warn == TRUE && ups_temperature>=warning_value) {
300 result = max_state (result, STATE_WARNING);
301 }
302 asprintf (&data, "%s %s", data,
303 perfdata ("temp", (long)ups_temperature, tunits,
304 check_warn, (long)(1000*warning_value),
305 check_crit, (long)(1000*critical_value),
306 TRUE, 0, FALSE, 0));
307 } else {
308 asprintf (&data, "%s %s", data,
309 perfdata ("temp", (long)ups_temperature, tunits,
310 FALSE, 0, FALSE, 0, TRUE, 0, FALSE, 0));
311 }
312 }
314 /* if the UPS does not support any options we are looking for, report an error */
315 if (supported_options == UPS_NONE) {
316 result = STATE_CRITICAL;
317 asprintf (&message, _("UPS does not support any available options\n"));
318 }
320 /* reset timeout */
321 alarm (0);
323 printf ("UPS %s - %s|%s\n", state_text(result), message, data);
324 return result;
325 }
329 /* determines what options are supported by the UPS */
330 int
331 determine_status (void)
332 {
333 char recv_buffer[MAX_INPUT_BUFFER];
334 char temp_buffer[MAX_INPUT_BUFFER];
335 char *ptr;
336 int res;
338 res=get_ups_variable ("ups.status", recv_buffer, sizeof (recv_buffer));
339 if (res == NOSUCHVAR) return OK;
340 if (res != STATE_OK) {
341 printf (_("Invalid response received from host\n"));
342 return ERROR;
343 }
345 supported_options |= UPS_STATUS;
347 strcpy (temp_buffer, recv_buffer);
348 for (ptr = (char *) strtok (temp_buffer, " "); ptr != NULL;
349 ptr = (char *) strtok (NULL, " ")) {
350 if (!strcmp (ptr, "OFF"))
351 status |= UPSSTATUS_OFF;
352 else if (!strcmp (ptr, "OL"))
353 status |= UPSSTATUS_OL;
354 else if (!strcmp (ptr, "OB"))
355 status |= UPSSTATUS_OB;
356 else if (!strcmp (ptr, "LB"))
357 status |= UPSSTATUS_LB;
358 else if (!strcmp (ptr, "CAL"))
359 status |= UPSSTATUS_CAL;
360 else if (!strcmp (ptr, "RB"))
361 status |= UPSSTATUS_RB;
362 else if (!strcmp (ptr, "BYPASS"))
363 status |= UPSSTATUS_BYPASS;
364 else if (!strcmp (ptr, "OVER"))
365 status |= UPSSTATUS_OVER;
366 else if (!strcmp (ptr, "TRIM"))
367 status |= UPSSTATUS_TRIM;
368 else if (!strcmp (ptr, "BOOST"))
369 status |= UPSSTATUS_BOOST;
370 else if (!strcmp (ptr, "CHRG"))
371 status |= UPSSTATUS_CHRG;
372 else if (!strcmp (ptr, "DISCHRG"))
373 status |= UPSSTATUS_DISCHRG;
374 else
375 status |= UPSSTATUS_UNKOWN;
376 }
378 return OK;
379 }
382 /* gets a variable value for a specific UPS */
383 int
384 get_ups_variable (const char *varname, char *buf, size_t buflen)
385 {
386 /* char command[MAX_INPUT_BUFFER]; */
387 char temp_buffer[MAX_INPUT_BUFFER];
388 char send_buffer[MAX_INPUT_BUFFER];
389 char *ptr;
390 int len;
392 *buf=0;
394 /* create the command string to send to the UPS daemon */
395 sprintf (send_buffer, "GET VAR %s %s\n", ups_name, varname);
397 /* send the command to the daemon and get a response back */
398 if (process_tcp_request
399 (server_address, server_port, send_buffer, temp_buffer,
400 sizeof (temp_buffer)) != STATE_OK) {
401 printf (_("Invalid response received from host\n"));
402 return ERROR;
403 }
405 ptr = temp_buffer;
406 len = strlen(ptr);
407 if (len > 0 && ptr[len-1] == '\n') ptr[len-1]=0;
408 if (strcmp (ptr, "ERR UNKNOWN-UPS") == 0) {
409 printf (_("CRITICAL - no such ups '%s' on that host\n"), ups_name);
410 return ERROR;
411 }
413 if (strcmp (ptr, "ERR VAR-NOT-SUPPORTED") == 0) {
414 /*printf ("Error: Variable '%s' is not supported\n", varname);*/
415 return NOSUCHVAR;
416 }
418 if (strcmp (ptr, "ERR DATA-STALE") == 0) {
419 printf (_("CRITICAL - UPS data is stale\n"));
420 return ERROR;
421 }
423 if (strncmp (ptr, "ERR", 3) == 0) {
424 printf (_("Unknown error: %s\n"), ptr);
425 return ERROR;
426 }
428 ptr = temp_buffer + strlen (varname) + strlen (ups_name) + 6;
429 len = strlen(ptr);
430 if (len < 2 || ptr[0] != '"' || ptr[len-1] != '"') {
431 printf (_("Error: unable to parse variable\n"));
432 return ERROR;
433 }
434 strncpy (buf, ptr+1, len - 2);
435 buf[len - 2] = 0;
437 return OK;
438 }
441 /* Command line: CHECK_UPS -H <host_address> -u ups [-p port] [-v variable]
442 [-wv warn_value] [-cv crit_value] [-to to_sec] */
445 /* process command-line arguments */
446 int
447 process_arguments (int argc, char **argv)
448 {
449 int c;
451 int option = 0;
452 static struct option longopts[] = {
453 {"hostname", required_argument, 0, 'H'},
454 {"ups", required_argument, 0, 'u'},
455 {"port", required_argument, 0, 'p'},
456 {"critical", required_argument, 0, 'c'},
457 {"warning", required_argument, 0, 'w'},
458 {"timeout", required_argument, 0, 't'},
459 {"temperature", no_argument, 0, 'T'},
460 {"variable", required_argument, 0, 'v'},
461 {"version", no_argument, 0, 'V'},
462 {"help", no_argument, 0, 'h'},
463 {0, 0, 0, 0}
464 };
466 if (argc < 2)
467 return ERROR;
469 for (c = 1; c < argc; c++) {
470 if (strcmp ("-to", argv[c]) == 0)
471 strcpy (argv[c], "-t");
472 else if (strcmp ("-wt", argv[c]) == 0)
473 strcpy (argv[c], "-w");
474 else if (strcmp ("-ct", argv[c]) == 0)
475 strcpy (argv[c], "-c");
476 }
478 while (1) {
479 c = getopt_long (argc, argv, "hVTH:u:p:v:c:w:t:", longopts,
480 &option);
482 if (c == -1 || c == EOF)
483 break;
485 switch (c) {
486 case '?': /* help */
487 usage2 (_("Unknown argument"), optarg);
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 usage4 (_("Timeout interval must be a positive integer"));
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 }
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 }
583 void
584 print_help (void)
585 {
586 char *myport;
587 asprintf (&myport, "%d", PORT);
589 print_revision (progname, revision);
591 printf ("Copyright (c) 2000 Tom Shields");
592 printf ("Copyright (c) 2004 Alain Richard <alain.richard@equation.fr>\n");
593 printf ("Copyright (c) 2004 Arnaud Quette <arnaud.quette@mgeups.com>\n");
594 printf (COPYRIGHT, copyright, email);
596 printf (_("This plugin tests the UPS service on the specified host.\n\
597 Network UPS Tools from www.networkupstools.org must be running for this\n\
598 plugin to work.\n\n"));
600 print_usage ();
602 printf (_(UT_HELP_VRSN));
604 printf (_(UT_HOST_PORT), 'p', myport);
606 printf (_("\
607 -u, --ups=STRING\n\
608 Name of UPS\n"));
610 printf (_("\
611 -T, --temperature\n\
612 Output of temperatures in Celsius\n"));
614 printf (_(UT_WARN_CRIT));
616 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
618 printf (_(UT_VERBOSE));
620 printf (_("\
621 This plugin attempts to determine the status of a UPS (Uninterruptible Power\n\
622 Supply) on a local or remote host. If the UPS is online or calibrating, the\n\
623 plugin will return an OK state. If the battery is on it will return a WARNING\n\
624 state. If the UPS is off or has a low battery the plugin will return a CRITICAL\n\
625 state.\n\n"));
627 printf (_("\
628 You may also specify a variable to check [such as temperature, utility voltage,\n\
629 battery load, etc.] as well as warning and critical thresholds for the value of\n\
630 that variable. If the remote host has multiple UPS that are being monitored you\n\
631 will have to use the [ups] option to specify which UPS to check.\n\n"));
633 printf (_("Notes:\n\n\
634 This plugin requires that the UPSD daemon distributed with Russel Kroll's\n\
635 Smart UPS Tools be installed on the remote host. If you do not have the\n\
636 package installed on your system, you can download it from\n\
637 http://www.networkupstools.org\n\n"));
639 printf (_(UT_SUPPORT));
640 }
643 void
644 print_usage (void)
645 {
646 printf ("\
647 Usage: %s -H host -u ups [-p port] [-v variable]\n\
648 [-wv warn_value] [-cv crit_value] [-to to_sec] [-T]\n", progname);
649 }