1 /*
2  * Check-Plugin for Nagios to check status of an APC-UPS
3  * monitored by APCUPSD
4  *
5  *  Written by Christian Masopust, November 2005
6  *
7  * Build it with: cc check_apcupsd.c ../lib/libapc.a -o check_apcupsd
8  *
9  * Execute: ./check_apcupsd [host[:port]]
10  *
11  */
12 
13 #include "apc.h"
14 
15 #ifdef HAVE_NISLIB
16 
17 /* Default values, can be changed on command line */
18 #define SERV_TCP_PORT 3551
19 #define SERV_HOST_ADDR "127.0.0.1"
20 
21 #define BIGBUF 4096
22 char statbuf[BIGBUF];
23 int statlen = BIGBUF;
24 
25 #define NAGIOS_OK       0
26 #define NAGIOS_WARNING  1
27 #define NAGIOS_CRITICAL 2
28 #define NAGIOS_UNKNOWN  3
29 #define S_NAGIOS_OK             "OK: "
30 #define S_NAGIOS_WARNING        "WARNING: "
31 #define S_NAGIOS_CRITICAL       "CRITICAL: "
32 #define S_NAGIOS_UNKNOWN        "UNKNOWN: "
33 
34 
35 /* List of variables that can be read by getupsvar()
36  * First field is that name given to getupsvar(),
37  * Second field is our internal name as produced by the STATUS
38  *   output from apcupsd.
39  * Third field, if 0 returns everything to the end of the
40  *    line, and if 1 returns only to first space (e.g. integers,
41  *    and floating point values.
42  */
43 static struct {
44    char *request;
45    char *upskeyword;
46    int nfields;
47 } cmdtrans[] = {
48    {"model",      "MODEL",    0},
49    {"upsmodel",   "UPSMODEL", 0},
50    {"date",       "DATE",     0},
51    {"battcap",    "BCHARGE",  1},
52    {"mbattchg",   "MBATTCHG", 1},
53    {"battvolt",   "BATTV",    1},
54    {"nombattv",   "NOMBATTV", 1},
55    {"utility",    "LINEV",    1},
56    {"upsload",    "LOADPCT",  1},
57    {"loadpct",    "LOADPCT",  1},
58    {"outputv",    "OUTPUTV",  1},
59    {"status",     "STATFLAG", 1},
60    {"linemin",    "MINLINEV", 1},
61    {"linemax",    "MAXLINEV", 1},
62    {"upstemp",    "ITEMP",    1},
63    {"outputfreq", "LINEFREQ", 1},
64    {"translo",    "LOTRANS",  1},
65    {"transhi",    "HITRANS",  1},
66    {"runtime",    "TIMELEFT", 1},
67    {"mintimel",   "MINTIMEL", 1},
68    {"retpct",     "RETPCT",   1},          /* min batt to turn on UPS */
69    {"sense",      "SENSE",    1},
70    {"hostname",   "HOSTNAME", 1},
71    {"battdate",   "BATTDATE", 1},
72    {"serialno",   "SERIALNO", 1},
73    {"lastxfer",   "LASTXFER", 0},          /* reason for last xfer to batteries */
74    {"selftest",   "SELFTEST", 1},          /* results of last self test */
75    {"laststest",  "LASTSTEST", 0},
76    {"version",    "VERSION",  1},
77    {"upsname",    "UPSNAME",  1},
78    {"lowbatt",    "DLOWBATT", 1},          /* low battery power off delay */
79    {"battpct",    "BCHARGE",  1},
80    {"highxfer",   "HITRANS",  1},
81    {"lowxfer",    "LOTRANS",  1},
82    {"cable",      "CABLE",    0},
83    {"firmware",   "FIRMWARE", 0},
84    {NULL, NULL}
85 };
86 
87 int fetch_data(char *host, int port);
88 int getupsvar(char *host, int port, char *request, char *answer, int anslen);
89 int fill_buffer(int sockfd);
90 
91 extern int net_errno;
92 
93 struct sockaddr_in tcp_serv_addr;
94 
error_abort(char * msg)95 void error_abort(char *msg)
96 {
97    fprintf(stdout, msg);
98    exit(NAGIOS_CRITICAL);
99 }
100 
main(int argc,char * argv[])101 int main(int argc, char *argv[])
102 {
103    int port;
104    char host[200];
105    char msg[200], *p;
106    char hostname[100];
107    char model[100];
108    char upsname[100];
109    char status[1000];
110    int iStatus;
111    char sStatus[10];
112    char loadpct[100];
113    char runtime[100];
114    int retVal;
115 
116    retVal = NAGIOS_UNKNOWN;
117    strcpy (sStatus, S_NAGIOS_UNKNOWN);
118 
119    strcpy(host, SERV_HOST_ADDR);
120    port = SERV_TCP_PORT;
121 
122    if (argc > 1) {
123       strcpy(host, argv[1]); /* get host from command line */
124       p = strchr(host, ':');
125       if (p) {
126          *p++ = 0;
127          port = atoi(p);
128       }
129    }
130 
131    if (getupsvar(host, port, "hostname", msg, sizeof(msg)) <= 0) {
132        printf("%scannot get hostname from UPS-Server\n", S_NAGIOS_CRITICAL);
133        exit(NAGIOS_CRITICAL);
134    }
135    strcpy(hostname, msg);
136 
137    if (getupsvar(host, port, "model", msg, sizeof(msg)) <= 0) {
138        printf("%scannot get model from UPS-Server\n", S_NAGIOS_CRITICAL);
139        exit(NAGIOS_CRITICAL);
140    }
141    strcpy(model, msg);
142 
143    if (getupsvar(host, port, "upsname", msg, sizeof(msg)) <= 0) {
144        printf("%scannot get upsname from UPS-Server\n", S_NAGIOS_CRITICAL);
145        exit(NAGIOS_CRITICAL);
146    }
147    strcpy(upsname, msg);
148 
149    if (getupsvar(host, port, "status", msg, sizeof(msg)) <= 0) {
150        printf("%scannot get status from UPS-Server\n", S_NAGIOS_CRITICAL);
151        exit(NAGIOS_CRITICAL);
152    }
153    iStatus = strtol(msg, 0, 16);
154    status[0] = '\0';
155    if (iStatus & UPS_calibration) {
156       strcat(status, "CALIBRATION ");
157       retVal = NAGIOS_OK;
158       strcpy(sStatus, S_NAGIOS_OK);
159    }
160    if (iStatus & UPS_trim) {
161       strcat(status, "SMART TRIM ");
162       retVal = NAGIOS_OK;
163       strcpy(sStatus, S_NAGIOS_OK);
164    }
165    if (iStatus & UPS_boost) {
166       strcat(status, "SMART BOOST ");
167       retVal = NAGIOS_OK;
168       strcpy(sStatus, S_NAGIOS_OK);
169    }
170    if (iStatus & UPS_online) {
171       strcat(status, "ONLINE ");
172       retVal = NAGIOS_OK;
173       strcpy(sStatus, S_NAGIOS_OK);
174    }
175    if (iStatus & UPS_onbatt) {
176       strcat(status, "ON BATTERY ");
177       retVal = NAGIOS_WARNING;
178       strcpy(sStatus, S_NAGIOS_WARNING);
179    }
180    if (iStatus & UPS_overload) {
181       strcat(status, "OVERLOADED ");
182       retVal = NAGIOS_CRITICAL;
183       strcpy(sStatus, S_NAGIOS_CRITICAL);
184    }
185    if (iStatus & UPS_battlow) {
186       strcat(status, "BATTERY LOW ");
187       retVal = NAGIOS_CRITICAL;
188       strcpy(sStatus, S_NAGIOS_CRITICAL);
189    }
190    if (iStatus & UPS_replacebatt) {
191       strcat(status, "REPLACE BATTERY ");
192       retVal = NAGIOS_WARNING;
193       strcpy(sStatus, S_NAGIOS_WARNING);
194    }
195    if (iStatus & UPS_commlost) {
196       strcat(status, "COMMUNICATION LOST ");
197       retVal = NAGIOS_CRITICAL;
198       strcpy(sStatus, S_NAGIOS_CRITICAL);
199    }
200    if (iStatus & UPS_shutdown) {
201       strcat(status, "SHUTDOWN ");
202       retVal = NAGIOS_OK;
203       strcpy(sStatus, S_NAGIOS_OK);
204    }
205    if (iStatus & UPS_slave) {
206       strcat(status, "SLAVE ");
207       retVal = NAGIOS_OK;
208       strcpy(sStatus, S_NAGIOS_OK);
209    }
210 
211    if (strlen(status) > 0) {
212       status[strlen(status) - 1] = '\0';
213    }
214 
215    if (getupsvar(host, port, "loadpct", msg, sizeof(msg)) <= 0) {
216        printf("%scannot get loadpct from UPS-Server\n", S_NAGIOS_CRITICAL);
217        exit(NAGIOS_CRITICAL);
218    }
219    strcpy(loadpct, msg);
220 
221    if (getupsvar(host, port, "runtime", msg, sizeof(msg)) <= 0) {
222        printf("%scannot get runtime from UPS-Server\n", S_NAGIOS_CRITICAL);
223        exit(NAGIOS_CRITICAL);
224    }
225    strcpy(runtime, msg);
226 
227    printf ("%sUPS: %s, Load: %s%%, Runtime: %smin, Status: %s\n", sStatus, model, loadpct, runtime, status);
228    /* printf("For host=%s ups=%s model=%s, the Status=%s, loadpct=%s, runtime=%s\n",
229        hostname, upsname, model, status, loadpct, runtime); */
230 
231    exit(retVal);
232 }
233 
234 
235 /*
236  * Read data into memory buffer to be used by getupsvar()
237  * Returns 0 on error
238  * Returns 1 if data fetched
239  */
fetch_data(char * host,int port)240 int fetch_data(char *host, int port)
241 {
242    int sockfd;
243    int stat;
244 
245    if ((sockfd = net_open(host, NULL, port)) < 0) {
246       printf("fetch_data: tcp_open failed for %s port %d", host, port);
247       return 0;
248    }
249 
250    stat = fill_buffer(sockfd);               /* fill statbuf */
251    net_close(sockfd);
252    return stat;
253 
254 }
255 
256 /*
257  *
258  * Returns 1 if var found
259  *   answer has var
260  * Returns 0 if variable name not found
261  *   answer has "Not found" is variable name not found
262  *   answer may have "N/A" if the UPS does not support this
263  *       feature
264  * Returns -1 if network problem
265  *   answer has "N/A" if host is not available or network error
266  */
getupsvar(char * host,int port,char * request,char * answer,int anslen)267 int getupsvar(char *host, int port, char *request, char *answer, int anslen)
268 {
269     int i;
270     char *stat_match = NULL;
271     char *find;
272     int nfields = 0;
273 
274     if (!fetch_data(host, port)) {
275         strcpy(answer, "N/A");
276         return -1;
277     }
278 
279     for (i=0; cmdtrans[i].request; i++)
280         if (!(strcmp(cmdtrans[i].request, request))) {
281              stat_match = cmdtrans[i].upskeyword;
282              nfields = cmdtrans[i].nfields;
283         }
284 
285     if (stat_match != NULL) {
286         if ((find=strstr(statbuf, stat_match)) != NULL) {
287              if (nfields == 1)  /* get one field */
288                  sscanf (find, "%*s %*s %s", answer);
289              else {             /* get everything to eol */
290                  i = 0;
291                  find += 11;  /* skip label */
292                  while (*find != '\n')
293                      answer[i++] = *find++;
294                  answer[i] = 0;
295              }
296              if (strcmp(answer, "N/A") == 0)
297                  return 0;
298              return 1;
299         }
300     }
301 
302     strcpy(answer, "Not found");
303     return 0;
304 }
305 
306 #define MAXLINE 512
307 
308 /* Fill buffer with data from UPS network daemon
309  * Returns 0 on error
310  * Returns 1 if OK
311  */
fill_buffer(int sockfd)312 int fill_buffer(int sockfd)
313 {
314    int n, stat = 1;
315    char buf[1000];
316 
317    statbuf[0] = 0;
318    statlen = 0;
319    if (net_send(sockfd, "status", 6) != 6) {
320       printf("fill_buffer: write error on socket\n");
321       return 0;
322    }
323 
324    while ((n = net_recv(sockfd, buf, sizeof(buf)-1)) > 0) {
325       buf[n] = 0;
326       strcat(statbuf, buf);
327    }
328    if (n < 0)
329       stat = 0;
330 
331    statlen = strlen(statbuf);
332    return stat;
333 
334 }
335 
336 #else /* HAVE_NISLIB */
337 
main(int argc,char * argv[])338 int main(int argc, char *argv[]) {
339     printf("Sorry, NIS code is not compiled in apcupsd.\n");
340     return 1;
341 }
342 
343 #endif /* HAVE_NISLIB */
344