Code

Patch from Arnaud Quette to bring support to NUT 2.0 plus couple improvements and...
[nagiosplug.git] / plugins / check_ups.c
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>
11  
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$
27  
28 ******************************************************************************/
30 const char *progname = "check_ups";
31 const char *revision = "$Revision$";
32 const char *copyright = "2000-2002";
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_OK;
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) != OK)
115                 usage (_("check_ups: could not parse arguments\n"));
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;
327 /* determines what options are supported by the UPS */
328 int
329 determine_status (void)
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;
380 /* gets a variable value for a specific UPS  */
381 int
382 get_ups_variable (const char *varname, char *buf, size_t buflen)
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;
391         
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;
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)
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();
573 int
574 validate_arguments (void)
576         if (! ups_name) {
577                 printf ("Error : no ups indicated\n");
578                 return ERROR;
579         }
580         return OK;
585 void
586 print_help (void)
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));
647 void
648 print_usage (void)
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);