1 /* upsimage - cgi program to create graphical ups information reports
2 
3    Status:
4      20020814 - Simon Rozman
5        - redesigned the meters
6      20020823 - Simon Rozman
7        - added support for width, height and scale_height parameters
8        - added support for outvolt
9        - noimage now writes out a clue, why upsimage failed
10      20020902 - Simon Rozman
11        - background now transparent by default
12        - added support for colorization parameters
13        - removed linear antialiasing of the scale, until I come up with a better algorithm
14      20020913 - Simon Rozman
15        - added width, height and scale_height to imgarg table
16      20020928 - Simon Rozman
17        - added imgvar table to hold description, how to draw each UPS variable supported
18        - added support for ACFREQ, OUT_FREQ and UPSTEMP
19 
20    Copyrights:
21      (C) 1998  Russell Kroll <rkroll@exploits.org>
22      (C) 2002  Simon Rozman <simon@rozman.net>
23 
24    This program is free software; you can redistribute it and/or modify
25    it under the terms of the GNU General Public License as published by
26    the Free Software Foundation; either version 2 of the License, or
27    (at your option) any later version.
28 
29    This program is distributed in the hope that it will be useful,
30    but WITHOUT ANY WARRANTY; without even the implied warranty of
31    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32    GNU General Public License for more details.
33 
34    You should have received a copy of the GNU General Public License
35    along with this program; if not, write to the Free Software
36    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37  */
38 
39 #include "common.h"
40 #include "upsclient.h"
41 #include "cgilib.h"
42 #include <stdlib.h>
43 #include <gd.h>
44 #include <gdfontmb.h>
45 
46 #include "nut_stdint.h"
47 #include "upsimagearg.h"
48 
49 #define MAX_CGI_STRLEN 64
50 
51 static	char	*monhost = NULL, *cmd = NULL;
52 
53 static	int	port;
54 static	char	*upsname, *hostname;
55 static	UPSCONN_t	ups;
56 
57 #define RED(x)		((x >> 16) & 0xff)
58 #define GREEN(x)	((x >> 8)  & 0xff)
59 #define BLUE(x)		(x & 0xff)
60 
61 
parsearg(char * var,char * value)62 void parsearg(char *var, char *value)
63 {
64 	long long	i, v;	/* Be big enough to fit all expected inputs; truncate later */
65 
66 	/* avoid bogus junk from evil people */
67 	if ((strlen(var) > MAX_CGI_STRLEN) || (strlen(value) > MAX_CGI_STRLEN))
68 		return;
69 
70 	if (!strcmp(var, "host")) {
71 		free(monhost);
72 		monhost = xstrdup(value);
73 		return;
74 	}
75 
76 	if (!strcmp(var, "display")) {
77 		free(cmd);
78 		cmd = xstrdup(value);
79 		return;
80 	}
81 
82 	/* see if this is one of the shared (upsimagearg.h) variables */
83 	for (i = 0; imgarg[i].name != NULL; i++) {
84 		if (!strcmp(imgarg[i].name, var)) {
85 			if (!strncmp(value, "0x", 2))
86 				v = (long long)strtoul(value + 2, (char **)NULL, 16);
87 			else
88 				v = (long long)atoi(value);
89 
90 			/* avoid false numbers from bad people */
91 			if (v < imgarg[i].min)
92 				imgarg[i].val = imgarg[i].min;
93 			else if (v > imgarg[i].max)
94 				imgarg[i].val = imgarg[i].max;
95 			else {
96 				assert (v < INT_MAX);
97 				assert (v > INT_MIN);
98 				imgarg[i].val = (int)v;
99 			}
100 			return;
101 		}
102 	}
103 }
104 
105 /* return the value from the URL or the default if it wasn't set */
get_imgarg(const char * name)106 static int get_imgarg(const char *name)
107 {
108 	int	i;
109 
110 	for (i = 0; imgarg[i].name != NULL; i++)
111 		if (!strcmp(imgarg[i].name, name))
112 			return imgarg[i].val;
113 
114 	return -1;
115 }
116 
117 /* write the HTML header then have gd dump the image */
118 static void drawimage(gdImagePtr im)
119 	__attribute__((noreturn));
120 
drawimage(gdImagePtr im)121 static void drawimage(gdImagePtr im)
122 {
123 	printf("Pragma: no-cache\n");
124 	printf("Content-type: image/png\n\n");
125 
126 	gdImagePng(im, stdout);
127 	gdImageDestroy(im);
128 
129 	upscli_disconnect(&ups);
130 
131 	exit(EXIT_SUCCESS);
132 }
133 
134 /* helper function to allocate color in the image */
color_alloc(gdImagePtr im,int rgb)135 static int color_alloc(gdImagePtr im, int rgb)
136 {
137 	return gdImageColorAllocate(im, RED(rgb), GREEN(rgb), BLUE(rgb));
138 }
139 
140 /* draws the scale behind the bar indicator */
drawscale(gdImagePtr im,int lvllo,int lvlhi,int step,int step5,int step10,int redlo1,int redhi1,int redlo2,int redhi2,int grnlo,int grnhi)141 static void drawscale(
142 	gdImagePtr im,				/* image where we would like to draw scale */
143 	int lvllo, int lvlhi,			/* min and max numbers on the scale */
144 	int step, int step5, int step10,	/* steps for minor, submajor and major dashes */
145 	int redlo1, int redhi1,			/* first red zone start and end */
146 	int redlo2, int redhi2,			/* second red zone start and end */
147 	int grnlo, int grnhi)			/* green zone start and end */
148 {
149 	int	col1, col2, back_color, scale_num_color, ok_zone_maj_color,
150 		ok_zone_min_color, neutral_zone_maj_color,
151 		neutral_zone_min_color, warn_zone_maj_color,
152 		warn_zone_min_color;
153 	char		lbltxt[SMALLBUF];
154 	int		y, level, range;
155 	int		width, height, scale_height;
156 
157 	back_color		= color_alloc(im, get_imgarg("back_col"));
158 	scale_num_color		= color_alloc(im, get_imgarg("scale_num_col"));
159 	ok_zone_maj_color	= color_alloc(im, get_imgarg("ok_zone_maj_col"));
160 	ok_zone_min_color	= color_alloc(im, get_imgarg("ok_zone_min_col"));
161 	neutral_zone_maj_color	= color_alloc(im, get_imgarg("neutral_zone_maj_col"));
162 	neutral_zone_min_color	= color_alloc(im, get_imgarg("neutral_zone_min_col"));
163 	warn_zone_maj_color	= color_alloc(im, get_imgarg("warn_zone_maj_col"));
164 	warn_zone_min_color	= color_alloc(im, get_imgarg("warn_zone_min_col"));
165 
166 	width = get_imgarg("width");
167 	height = get_imgarg("height");
168 	scale_height = get_imgarg("scale_height");
169 
170 	/* start out with a background color and make it transparent */
171 	gdImageFilledRectangle(im, 0, 0, width, height, back_color);
172 	gdImageColorTransparent(im, back_color);
173 
174 	range = lvlhi - lvllo;
175 
176 	/* draw scale to correspond with the values */
177 	for (level = lvlhi; level >= lvllo; level -= step) {
178 		/* select dash RGB color according to the level */
179 		if (((redlo1 <= level) && (level <=redhi1)) ||
180 			((redlo2 <= level) && (level <=redhi2))) {
181 			col1 = warn_zone_maj_color;
182 			col2 = warn_zone_min_color;
183 		} else if ((grnlo <= level) && (level <= grnhi)) {
184 			col1 = ok_zone_maj_color;
185 			col2 = ok_zone_min_color;
186 		} else {
187 			col1 = neutral_zone_maj_color;
188 			col2 = neutral_zone_min_color;
189 		}
190 
191 		/* calculate integer value for y */
192 		y = scale_height * (lvlhi - level) / range;
193 
194 		/* draw major, semimajor or minor dash accordingly */
195 		if (level % step10 == 0) {
196 			gdImageLine(im, 0, y, width, y, col1);
197 		} else {
198 			if (level % step5 == 0)
199 				gdImageLine(im, 5, y, width - 5, y, col2);
200 			else
201 				gdImageLine(im, 10, y, width - 10, y, col2);
202 		}
203 	}
204 
205 	/* put the values on the scale */
206 	for (level = lvlhi; level >= lvllo; level -= step) {
207 		if (level % step10 == 0) {
208 			y = scale_height * (lvlhi - level) / range;
209 			snprintf(lbltxt, sizeof(lbltxt), "%d", level);
210 			gdImageString(im, gdFontMediumBold,
211 				width - (int)(strlen(lbltxt)) * gdFontMediumBold->w,
212 				y, (unsigned char *) lbltxt, scale_num_color);
213 		}
214 	}
215 }
216 
217 /* draws the bar style indicator */
218 static void drawbar(
219 	int lvllo, int lvlhi,			/* min and max numbers on the scale */
220 	int step, int step5, int step10,	/* steps for minor, submajor and major dashes */
221 	int redlo1, int redhi1,			/* first red zone start and end */
222 	int redlo2, int redhi2,			/* second red zone start and end */
223 	int grnlo, int grnhi,			/* green zone start and end */
224 	double value, 				/* UPS variable value to draw */
225 	const char *format			/* printf style format to be used when rendering summary text */
226 )
227 	__attribute__((noreturn));
228 
drawbar(int lvllo,int lvlhi,int step,int step5,int step10,int redlo1,int redhi1,int redlo2,int redhi2,int grnlo,int grnhi,double value,const char * format)229 static void drawbar(
230 	int lvllo, int lvlhi,			/* min and max numbers on the scale */
231 	int step, int step5, int step10,	/* steps for minor, submajor and major dashes */
232 	int redlo1, int redhi1,			/* first red zone start and end */
233 	int redlo2, int redhi2,			/* second red zone start and end */
234 	int grnlo, int grnhi,			/* green zone start and end */
235 	double value, 				/* UPS variable value to draw */
236 	const char *format			/* printf style format to be used when rendering summary text */
237 )
238 {
239 	gdImagePtr	im;
240 	int		bar_color, summary_color;
241 	char		text[SMALLBUF];
242 	int		bar_y;
243 	int		width, height, scale_height;
244 
245 	/* get the dimension parameters */
246 	width = get_imgarg("width");
247 	height = get_imgarg("height");
248 	scale_height = get_imgarg("scale_height");
249 
250 	/* create the image */
251 	im = gdImageCreate(width, height);
252 
253 	/* draw the scale */
254 	drawscale(im, lvllo, lvlhi, step, step5, step10, redlo1, redhi1,
255 		redlo2, redhi2, grnlo, grnhi);
256 
257 	/* allocate colors for the bar and summary text */
258 	bar_color	= color_alloc(im, get_imgarg("bar_col"));
259 	summary_color	= color_alloc(im, get_imgarg("summary_col"));
260 
261 	/* rescale UPS value to fit in the scale */
262 	bar_y = (int)((1.0 - (value - lvllo) / (lvlhi - lvllo)) * scale_height);
263 
264 	/* sanity checks: */
265 
266 	/* 1: if value is above maximum, then bar_y goes negative */
267 	if (bar_y < 0)
268 		bar_y = 0;
269 
270 	/* 2: if value is below minimum, bar_y goes off the scale */
271 	if (bar_y > scale_height)
272 		bar_y = scale_height;
273 
274 	/* draw it */
275 	gdImageFilledRectangle(im, 25, bar_y, width - 25, scale_height,
276 		bar_color);
277 
278 	/* stick the text version of the value at the bottom center */
279 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
280 #pragma GCC diagnostic push
281 #endif
282 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
283 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
284 #endif
285 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
286 #pragma GCC diagnostic ignored "-Wformat-security"
287 #endif
288 	snprintf(text, sizeof(text), format, value);
289 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
290 #pragma GCC diagnostic pop
291 #endif
292 	gdImageString(im, gdFontMediumBold,
293 		(width - (int)(strlen(text))*gdFontMediumBold->w)/2,
294 		height - gdFontMediumBold->h,
295 		(unsigned char *) text, summary_color);
296 
297 	drawimage(im);
298 
299 	/* NOTREACHED */
300 }
301 
302 /* draws the error image */
303 static void noimage(const char *fmt, ...)
304 	__attribute__((noreturn));
305 
noimage(const char * fmt,...)306 static void noimage(const char *fmt, ...)
307 {
308 	gdImagePtr	im;
309 	int		back_color, summary_color;
310 	int		width, height;
311 	char		msg[SMALLBUF];
312 	va_list		ap;
313 
314 	va_start(ap, fmt);
315 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
316 #pragma GCC diagnostic push
317 #endif
318 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
319 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
320 #endif
321 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
322 #pragma GCC diagnostic ignored "-Wformat-security"
323 #endif
324 	vsnprintf(msg, sizeof(msg), fmt, ap);
325 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
326 #pragma GCC diagnostic pop
327 #endif
328 	va_end(ap);
329 
330 	width = get_imgarg("width");
331 	height = get_imgarg("height");
332 
333 	im = gdImageCreate(width, height);
334 	back_color = color_alloc(im, get_imgarg("back_col"));
335 	summary_color = color_alloc(im, get_imgarg("summary_col"));
336 
337 	gdImageFilledRectangle(im, 0, 0, width, height, back_color);
338 	gdImageColorTransparent(im, back_color);
339 
340 	if (width > height)
341 		gdImageString(im, gdFontMediumBold,
342 			(width - (int)(strlen(msg))*gdFontMediumBold->w)/2,
343 			(height - gdFontMediumBold->h)/2,
344 			(unsigned char *) msg, 	summary_color);
345 	else
346 		gdImageStringUp(im, gdFontMediumBold,
347 			(width - gdFontMediumBold->h)/2,
348 			(height + (int)(strlen(msg))*gdFontMediumBold->w)/2,
349 			(unsigned char *) msg, summary_color);
350 
351 	drawimage(im);
352 
353 	/* NOTE: Earlier code called noimage() and then exit(EXIT_FAILURE);
354 	 * to signal an error via process exit code. Now that drawimage()
355 	 * always ends with exit(EXIT_SUCCESS) - which might make webserver
356 	 * feel good - the command-line use if any suffers no error returns.
357 	 */
358 
359 	/* NOTREACHED */
360 }
361 
362 /* draws bar indicator when minimum, nominal or maximum values for the given
363    UPS variable can be determined.
364    deviation < 0 means that values below nom should be grey instead of
365    green */
366 static void drawgeneralbar(double var, int min, int nom, int max,
367 		int deviation, 	const char *format)
368 	__attribute__((noreturn));
369 
drawgeneralbar(double var,int min,int nom,int max,int deviation,const char * format)370 static void drawgeneralbar(double var, int min, int nom, int max,
371 		int deviation, 	const char *format)
372 {
373 	int	hi, lo, step1, step5, step10, graybelownom=0;
374 
375 	if(deviation < 0) {
376 		deviation=-deviation;
377 		graybelownom=1;
378 	}
379 
380 	if ((nom == -1) && ((min == -1) || (max == -1)))
381 		noimage("Can't determine range");
382 
383 	/* if min, max and nom are mixed up, arrange them appropriately */
384 	if (nom != -1) {
385 		if (min == -1)
386 			min = nom - 3*deviation;
387 
388 		if (max == -1)
389 			max = nom + 3*deviation;
390 	} else {
391 		/* if nominal value isn't available, assume, it's the
392 		   average between min and max */
393 		nom = (min + max) / 2;
394 	}
395 
396 	/* draw scale in the background */
397 	if ((max - min) <= 50) {
398 		/* the scale is sparse enough to draw finer scale */
399 		step1 = 1;
400 		step5 = 5;
401 		step10 = 10;
402 	} else if((max - min) <= 100) {
403 		step1 = 2;
404 		step5 = 10;
405 		step10 = 20;
406 	} else {
407 		step1 = 5;
408 		step5 = 20;
409 		step10 = 40;
410 	}
411 
412 	/* round min and max points to get high and low numbers for graph */
413 	lo = ((min - deviation) / step10) * step10;
414 	hi = ((max + deviation + step10/2) / step10) * step10;
415 
416 	if(!graybelownom) {
417 		drawbar(lo, hi, step1, step5, step10, max, hi, lo, min,
418 				nom - deviation, nom + deviation, var, format);
419 	}
420 	else {
421 		drawbar(lo, hi, step1, step5, step10, 0, min, max, hi,
422 				nom, max, var, format);
423 	}
424 
425 	/* NOTREACHED */
426 }
427 
428 /* draws input and output voltage bar style indicators */
429 static void draw_utility(double var, int min, int nom, int max,
430 		int deviation, const char *format)
431 	__attribute__((noreturn));
432 
draw_utility(double var,int min,int nom,int max,int deviation,const char * format)433 static void draw_utility(double var, int min, int nom, int max,
434 		int deviation, const char *format)
435 {
436 	/* hack: deal with hardware that doesn't have known transfer points */
437 	if (min == -1) {
438 		if(var < 200) {
439 			min = 90;
440 		}
441 		else if(var < 300) {
442 			min = 200;
443 		}
444 		else {
445 			min = 340;
446 		}
447 	}
448 
449 	/* somewhere between 220 and 230 V, to keep everybody satisfied */
450 	if (nom == -1) {
451 		if(var < 200) {
452 			nom = 110;
453 		}
454 		else if(var < 300) {
455 			nom = 225;
456 		}
457 		else {
458 			nom = 400;
459 		}
460 	}
461 
462 	/* symmetrical around nom */
463 	if (max == -1)
464 		max = nom+(nom-min);
465 
466 	/* Acceptable range of voltage is 85%-110% of nominal voltage
467 	 * in EU at least. Be conservative and say +-10% */
468 	deviation = (int)(nom * 0.1);
469 
470 	drawgeneralbar(var, min, nom, max, deviation, format);
471 
472 	/* NOTREACHED */
473 }
474 
475 /* draws battery.percent bar style indicator */
476 static void draw_battpct(double var, int min, int nom,
477 		int max, int deviation, const char *format)
478 	__attribute__((noreturn));
479 
draw_battpct(double var,int min,int nom,int max,int deviation,const char * format)480 static void draw_battpct(double var, int min, int nom,
481 		int max, int deviation, const char *format)
482 {
483 	NUT_UNUSED_VARIABLE(nom);
484 	NUT_UNUSED_VARIABLE(max);
485 	NUT_UNUSED_VARIABLE(deviation);
486 
487 	if (min < 0) {
488 		min = 50;
489 	}
490 
491 	drawbar(0, 100, 2, 10, 20, 0, min, -1, -1, 80, 100, var, format);
492 }
493 
494 /* draws battery.voltage bar style indicator */
495 static void draw_battvolt(double var, int min, int nom, int max,
496 		int deviation, const char *format)
497 	__attribute__((noreturn));
498 
draw_battvolt(double var,int min,int nom,int max,int deviation,const char * format)499 static void draw_battvolt(double var, int min, int nom, int max,
500 		int deviation, const char *format)
501 {
502 	if(nom == -1) {
503 		/* Use a fixed set of reasonable nominal voltages, seems to
504 		 * be the only way to get reasonable behaviour during
505 		 * discharge */
506 
507 		if(var < 9)
508 			nom = 6;
509 		else if(var < 18)
510 			nom = 12;
511 		else if(var < 30)
512 			nom = 24;
513 		else if(var < 60)
514 			nom = 48;
515 		else if(var < 120)
516 			nom = 96;
517 		else if(var < 230)
518 			nom = 192;
519 		else
520 			nom = 384;
521 
522 	}
523 
524 	if(min == -1) {
525 		min = (int)(nom/2*1.6+1); /* Assume a 2V cell is dead at 1.6V */
526 	}
527 
528 	if(max == -1) {
529 		max = (int)(nom/2*2.3+1); /* Assume 2.3V float charge voltage */
530 	}
531 
532 	if (nom < min || nom > max)
533 		nom = -1;
534 
535 	deviation = (int)(-nom*0.05); /* 5% deviation from nominal voltage */
536 	if(deviation==0) {
537 		deviation = -1;
538 	}
539 
540 	drawgeneralbar(var, min, nom, max, deviation, format);
541 }
542 
543 /* draws ups.load bar style indicator */
544 static void draw_upsload(double var, int min,
545 		int nom, int max,
546 		int deviation, const char *format)
547 	__attribute__((noreturn));
548 
draw_upsload(double var,int min,int nom,int max,int deviation,const char * format)549 static void draw_upsload(double var, int min,
550 		int nom, int max,
551 		int deviation, const char *format)
552 {
553 	NUT_UNUSED_VARIABLE(min);
554 	NUT_UNUSED_VARIABLE(nom);
555 	NUT_UNUSED_VARIABLE(max);
556 	NUT_UNUSED_VARIABLE(deviation);
557 
558 	drawbar(0, 125, 5, 5, 25, 100, 125, -1, -1, 0, 50, var, format);
559 }
560 
561 /* draws temperature bar style indicator */
562 static void draw_temperature(double var, int min, int nom, int max,
563 		int deviation, const char *format)
564 	__attribute__((noreturn));
565 
draw_temperature(double var,int min,int nom,int max,int deviation,const char * format)566 static void draw_temperature(double var, int min, int nom, int max,
567 		int deviation, const char *format)
568 {
569 	int	hi = get_imgarg("tempmax");
570 	int	lo = get_imgarg("tempmin");
571 	NUT_UNUSED_VARIABLE(nom);
572 	NUT_UNUSED_VARIABLE(deviation);
573 
574 	drawbar(lo, hi, 1, 5, 10, lo, min, max, hi, -1, -1, var, format);
575 }
576 
577 /* draws humidity bar style indicator */
578 static void draw_humidity(double var, int min, int nom, int max,
579 		int deviation, const char *format)
580 	__attribute__((noreturn));
581 
draw_humidity(double var,int min,int nom,int max,int deviation,const char * format)582 static void draw_humidity(double var, int min, int nom, int max,
583 		int deviation, const char *format)
584 {
585 	NUT_UNUSED_VARIABLE(nom);
586 	NUT_UNUSED_VARIABLE(deviation);
587 
588 	drawbar(0, 100, 2, 10, 20, 0, min, max, 100, -1, -1, var, format);
589 }
590 
get_var(const char * var,char * buf,size_t buflen)591 static int get_var(const char *var, char *buf, size_t buflen)
592 {
593 	int	ret;
594 	size_t	numq, numa;
595 	const	char	*query[4];
596 	char	**answer;
597 
598 	query[0] = "VAR";
599 	query[1] = upsname;
600 	query[2] = var;
601 
602 	numq = 3;
603 
604 	ret = upscli_get(&ups, numq, query, &numa, &answer);
605 
606 	if (ret < 0)
607 		return 0;
608 
609 	if (numa < numq)
610 		return 0;
611 
612 	snprintf(buf, buflen, "%s", answer[3]);
613 	return 1;
614 }
615 
main(int argc,char ** argv)616 int main(int argc, char **argv)
617 {
618 	char	str[SMALLBUF];
619 	int	i, min, nom, max;
620 	double	var = 0;
621 	NUT_UNUSED_VARIABLE(argc);
622 	NUT_UNUSED_VARIABLE(argv);
623 
624 	extractcgiargs();
625 
626 	/* no 'host=' or 'display=' given */
627 	if ((!monhost) || (!cmd))
628 		noimage("No host or display");
629 
630 	if (!checkhost(monhost, NULL))
631 		noimage("Access denied");
632 
633 	upsname = hostname = NULL;
634 
635 	if (upscli_splitname(monhost, &upsname, &hostname, &port) != 0) {
636 		noimage("Invalid UPS definition (upsname[@hostname[:port]])\n");
637 #ifndef HAVE___ATTRIBUTE__NORETURN
638 		exit(EXIT_FAILURE);	/* Should not get here in practice, but compiler is afraid we can fall through */
639 #endif
640 	}
641 
642 	if (upscli_connect(&ups, hostname, port, 0) < 0) {
643 		noimage("Can't connect to server:\n%s\n",
644 			upscli_strerror(&ups));
645 #ifndef HAVE___ATTRIBUTE__NORETURN
646 		exit(EXIT_FAILURE);	/* Should not get here in practice, but compiler is afraid we can fall through */
647 #endif
648 	}
649 
650 	for (i = 0; imgvar[i].name; i++)
651 		if (!strcmp(cmd, imgvar[i].name)) {
652 
653 			/* sanity check whether we have draw function
654 			   registered with this variable */
655 			if (!imgvar[i].drawfunc) {
656 				noimage("Draw function N/A");
657 #ifndef HAVE___ATTRIBUTE__NORETURN
658 				exit(EXIT_FAILURE);	/* Should not get here in practice, but compiler is afraid we can fall through */
659 #endif
660 			}
661 
662 			/* get the variable value */
663 			if (get_var(imgvar[i].name, str, sizeof(str)) == 1) {
664 				var = strtod(str, NULL);
665 			} else {
666 				/* no value, no fun */
667 				snprintf(str, sizeof(str), "%s N/A",
668 					imgvar[i].name);
669 				noimage(str);
670 #ifndef HAVE___ATTRIBUTE__NORETURN
671 				exit(EXIT_FAILURE);	/* Should not get here in practice, but compiler is afraid we can fall through */
672 #endif
673 			}
674 
675 			/* when getting minimum, nominal and maximum values,
676 			   we first look if the marginal value is supported
677 			   by the UPS driver, if not, we look it up in the
678 			   imgarg table under the SAME name */
679 
680 			/* get the minimum value */
681 			if (imgvar[i].minimum) {
682 				if (get_var(imgvar[i].minimum, str,
683 					sizeof(str)) == 1) {
684 					min = atoi(str);
685 				} else {
686 					min = get_imgarg(imgvar[i].minimum);
687 				}
688 
689 			} else {
690 				min = -1;
691 			}
692 
693 			/* get the nominal value */
694 			if (imgvar[i].nominal) {
695 				if (get_var(imgvar[i].nominal, str,
696 					sizeof(str)) == 1) {
697 					nom = atoi(str);
698 				} else {
699 					nom = get_imgarg(imgvar[i].nominal);
700 				}
701 
702 			} else {
703 				nom = -1;
704 			}
705 
706 			/* get the maximum value */
707 			if (imgvar[i].maximum) {
708 				if (get_var(imgvar[i].maximum, str,
709 					sizeof(str)) == 1) {
710 					max = atoi(str);
711 				} else {
712 					max = get_imgarg(imgvar[i].maximum);
713 				}
714 
715 			} else {
716 				max = -1;
717 			}
718 
719 			imgvar[i].drawfunc(var, min, nom, max,
720 				imgvar[i].deviation, imgvar[i].format);
721 			exit(EXIT_SUCCESS);
722 		}
723 
724 	noimage("Unknown display");
725 #ifndef HAVE___ATTRIBUTE__NORETURN
726 	exit(EXIT_FAILURE);
727 #endif
728 }
729 
730 imgvar_t imgvar[] = {
731 	{ "input.voltage", "input.transfer.low", "input.voltage.nominal",
732 		"input.transfer.high", 0,
733 		"%.1f VAC", draw_utility				},
734 
735 	{ "input.L1-N.voltage", "input.transfer.low", "input.voltage.nominal",
736 		"input.transfer.high", 0,
737 		"%.1f VAC", draw_utility				},
738 
739 	{ "input.L2-N.voltage", "input.transfer.low", "input.voltage.nominal",
740 		"input.transfer.high", 0,
741 		"%.1f VAC", draw_utility				},
742 
743 	{ "input.L3-N.voltage", "input.transfer.low", "input.voltage.nominal",
744 		"input.transfer.high", 0,
745 		"%.1f VAC", draw_utility				},
746 
747 	{ "input.L1-L2.voltage", "input.transfer.low", "input.voltage.nominal",
748 		"input.transfer.high", 0,
749 		"%.1f VAC", draw_utility				},
750 
751 	{ "input.L2-L3.voltage", "input.transfer.low", "input.voltage.nominal",
752 		"input.transfer.high", 0,
753 		"%.1f VAC", draw_utility				},
754 
755 	{ "input.L3-L1.voltage", "input.transfer.low", "input.voltage.nominal",
756 		"input.transfer.high", 0,
757 		"%.1f VAC", draw_utility				},
758 
759 	{ "battery.charge", "battery.charge.low", NULL, NULL, 0,
760 		"%.1f %%",	draw_battpct				},
761 
762 	{ "battery.voltage", "battery.voltage.low", "battery.voltage.nominal",
763 		"battery.voltage.high", 0,
764 		"%.1f VDC",	draw_battvolt				},
765 
766 	/* We use 'high' ASCII for the degrees symbol, since the gdImageString()
767 	 * function doesn't understand UTF-8 or HTML escape sequences. :-( */
768 	{ "ups.temperature", "ups.temperature.low", NULL,
769 		"ups.temperature.high", 0,
770 		"%.1f \260C",	draw_temperature			},
771 
772 	/* Same here. */
773 	{ "ambient.temperature", "ambient.temperature.low", NULL,
774 		"ambient.temperature.high", 0,
775 		"%.1f \260C",	draw_temperature			},
776 
777 	{ "ambient.humidity", "ambient.humidity.low", NULL,
778 		"ambient.humidity.high", 0,
779 		"%.1f %%",	draw_humidity				},
780 
781 	{ "input.frequency", NULL, "input.frequency.nominal", NULL, 2,
782 		"%.1f Hz",	drawgeneralbar				},
783 
784 	{ "ups.load", NULL, NULL, NULL, 0,
785 		"%.1f %%",	draw_upsload				},
786 
787 	{ "output.L1.power.percent", NULL, NULL, NULL, 0,
788 		"%.1f %%",	draw_upsload				},
789 
790 	{ "output.L2.power.percent", NULL, NULL, NULL, 0,
791 		"%.1f %%",	draw_upsload				},
792 
793 	{ "output.L3.power.percent", NULL, NULL, NULL, 0,
794 		"%.1f %%",	draw_upsload				},
795 
796 	{ "output.L1.realpower.percent", NULL, NULL, NULL, 0,
797 		"%.1f %%",	draw_upsload				},
798 
799 	{ "output.L2.realpower.percent", NULL, NULL, NULL, 0,
800 		"%.1f %%",	draw_upsload				},
801 
802 	{ "output.L3.realpower.percent", NULL, NULL, NULL, 0,
803 		"%.1f %%",	draw_upsload				},
804 
805 	{ "output.voltage", "input.transfer.low", "output.voltage.nominal",
806 		"input.transfer.high", 0,
807 		"%.1f VAC",	draw_utility				},
808 
809 	{ "output.L1-N.voltage", "input.transfer.low",
810 	 	"output.voltage.nominal", "input.transfer.high", 0,
811 		"%.1f VAC",	draw_utility				},
812 
813 	{ "output.L2-N.voltage", "input.transfer.low",
814 	 	"output.voltage.nominal", "input.transfer.high", 0,
815 		"%.1f VAC",	draw_utility				},
816 
817 	{ "output.L3-N.voltage", "input.transfer.low",
818 	 	"output.voltage.nominal", "input.transfer.high", 0,
819 		"%.1f VAC",	draw_utility				},
820 
821 	{ "output.L1-L2.voltage", "input.transfer.low",
822 	 	"output.voltage.nominal", "input.transfer.high", 0,
823 		"%.1f VAC",	draw_utility				},
824 
825 	{ "output.L2-L3.voltage", "input.transfer.low",
826 	 	"output.voltage.nominal", "input.transfer.high", 0,
827 		"%.1f VAC",	draw_utility				},
828 
829 	{ "output.L3-L1.voltage", "input.transfer.low",
830 	 	"output.voltage.nominal", "input.transfer.high", 0,
831 		"%.1f VAC",	draw_utility				},
832 
833 	{ "output.frequency", NULL, "output.frequency.nominal", NULL, 2,
834 		"%.1f Hz",	drawgeneralbar				},
835 
836 	{ NULL,		NULL,		NULL,		NULL,		0,
837 		NULL,		NULL }
838 };
839