1 /*
2  * The dhcpd-pools has BSD 2-clause license which also known as "Simplified
3  * BSD License" or "FreeBSD License".
4  *
5  * Copyright 2006- Sami Kerola. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  *    1. Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *
14  *    2. Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in the
16  *       documentation and/or other materials provided with the
17  *       distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * The views and conclusions contained in the software and documentation are
32  * those of the authors and should not be interpreted as representing
33  * official policies, either expressed or implied, of Sami Kerola.
34  */
35 
36 /*! \file output.c
37  * \brief All about output formats.
38  */
39 
40 #include <config.h>
41 
42 #include <arpa/inet.h>
43 #include <errno.h>
44 #include <inttypes.h>
45 #include <langinfo.h>
46 #include <locale.h>
47 #include <math.h>
48 #include <netinet/in.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <sys/stat.h>
52 #include <time.h>
53 #include <unistd.h>
54 
55 #include "close-stream.h"
56 #include "error.h"
57 #include "progname.h"
58 #include "strftime.h"
59 
60 #include "dhcpd-pools.h"
61 
62 /*! \enum colored_formats
63  * \brief Enumeration of output formats.  Keep the text and html first, they
64  * are used color array selector.
65  */
66 enum colored_formats {
67 	OUT_FORM_TEXT,
68 	OUT_FORM_HTML,
69 	NUM_OF_OUT_FORMS
70 };
71 
72 /*! \enum count_status_t
73  * \brief Enumeration of possible range and shared net statuses.
74  */
75 enum count_status_t {
76 	STATUS_OK,
77 	STATUS_WARN,
78 	STATUS_CRIT,
79 	STATUS_IGNORED,
80 	STATUS_SUPPRESSED,
81 	COLOR_RESET
82 };
83 
84 /*! \var color_tags
85  * \brief Array of strings that make colors to start and end in different
86  * schemas per array column. */
87 static const char *color_tags[][NUM_OF_OUT_FORMS] = {
88 	[STATUS_OK]	    = { "",		"" },
89 	[STATUS_WARN]	    = { "\033[1;33m",	" style=\"color:magenta;font-style:italic\"" },
90 	[STATUS_CRIT]	    = { "\033[1;31m",	" style=\"color:red;font-weight:bold\"" },
91 	[STATUS_IGNORED]    = { "\033[1;32m",	" style=\"color:green\"" },
92 	[STATUS_SUPPRESSED] = { "\033[1;34m",	" style=\"color:blue\"" },
93 	[COLOR_RESET]	    = { "\033[0m",	"" }
94 };
95 
96 /*! \brief Calculate range percentages and such.
97  * \return Indicator if the entry should be skipped from output. */
range_output_helper(struct conf_t * state,struct output_helper_t * oh,struct range_t * range_p)98 int range_output_helper(struct conf_t *state, struct output_helper_t *oh,
99 			struct range_t *range_p)
100 {
101 	/* counts and calculations */
102 	oh->range_size = get_range_size(range_p);
103 	oh->percent = (double)(100 * range_p->count) / oh->range_size;
104 	oh->tc = range_p->touched + range_p->count;
105 	oh->tcp = (double)(100 * oh->tc) / oh->range_size;
106 	if (state->backups_found == 1) {
107 		oh->bup = (double)(100 * range_p->backups) / oh->range_size;
108 	}
109 	/* set status */
110 	oh->status = STATUS_OK;
111 	if (state->critical < oh->percent && (oh->range_size - range_p->count) < state->crit_count)
112 		oh->status = STATUS_CRIT;
113 	else if (state->warning < oh->percent
114 		 && (oh->range_size - range_p->count) < state->warn_count)
115 		oh->status = STATUS_WARN;
116 	if (oh->status != STATUS_OK) {
117 		if (oh->range_size <= state->minsize) {
118 			oh->status = STATUS_IGNORED;
119 			if (state->skip_minsize)
120 				return 1;
121 		} else if (state->snet_alarms && range_p->shared_net != state->shared_net_root) {
122 			oh->status = STATUS_SUPPRESSED;
123 			if (state->skip_suppressed)
124 				return 1;
125 		}
126 	}
127 	if ((state->skip_ok && oh->status == STATUS_OK) ||
128 	    (state->skip_warning && oh->status == STATUS_WARN) ||
129 	    (state->skip_critical && oh->status == STATUS_CRIT))
130 		return 1;
131 	return 0;
132 }
133 
134 /*! \brief Calculate shared network percentages and such.
135  * \return Indicator if the entry should be skipped from output. */
shnet_output_helper(struct conf_t * state,struct output_helper_t * oh,struct shared_network_t * shared_p)136 int shnet_output_helper(struct conf_t *state, struct output_helper_t *oh,
137 			struct shared_network_t *shared_p)
138 {
139 	/* counts and calculations */
140 	oh->tc = shared_p->touched + shared_p->used;
141 	if (fpclassify(shared_p->available) == FP_ZERO) {
142 		oh->percent = NAN;
143 		oh->tcp = NAN;
144 		oh->bup = NAN;
145 		oh->status = STATUS_SUPPRESSED;
146 		if (state->skip_suppressed)
147 			return 1;
148 		return 0;
149 	}
150 
151 	oh->percent = (double)(100 * shared_p->used) / shared_p->available;
152 	oh->tcp = (double)((100 * (shared_p->touched + shared_p->used)) / shared_p->available);
153 	if (state->backups_found == 1)
154 		oh->bup = (double)(100 * shared_p->backups) / shared_p->available;
155 
156 	/* set status */
157 	if (shared_p->available <= state->minsize) {
158 		oh->status = STATUS_IGNORED;
159 		if (state->skip_minsize)
160 			return 1;
161 	} else if (state->critical < oh->percent && (shared_p->available - shared_p->used) < state->crit_count) {
162 		oh->status = STATUS_CRIT;
163 		if (state->skip_critical)
164 			return 1;
165 	} else if (state->warning < oh->percent && (shared_p->available - shared_p->used) < state->warn_count) {
166 		oh->status = STATUS_WARN;
167 		if (state->skip_warning)
168 			return 1;
169 	} else {
170 		oh->status = STATUS_OK;
171 		if (state->skip_ok)
172 			return 1;
173 	}
174 	return 0;
175 
176 }
177 
178 /*! \brief Output a color based on output_helper_t status.
179  * \return Indicator whether coloring was started or not. */
start_color(struct conf_t * state,struct output_helper_t * oh,FILE * outfile)180 static int start_color(struct conf_t *state, struct output_helper_t *oh, FILE *outfile)
181 {
182 	if (oh->status == STATUS_OK) {
183 		return 0;
184 	}
185 	fputs(color_tags[oh->status][state->color_format], outfile);
186 	return 1;
187 }
188 
189 /*! \brief Helper function to open a output file.
190  * \return The outfile in all of the output functions. */
open_outfile(struct conf_t * state)191 static FILE *open_outfile(struct conf_t *state)
192 {
193 	FILE *outfile;
194 
195 	if (state->output_file) {
196 		outfile = fopen(state->output_file, "w+");
197 		if (outfile == NULL) {
198 			error(EXIT_FAILURE, errno, "open_outfile: %s", state->output_file);
199 		}
200 	} else {
201 		outfile = stdout;
202 	}
203 	return outfile;
204 }
205 
206 
207 /*! \brief Helper function to close outfile. */
close_outfile(FILE * outfile)208 static void close_outfile(FILE *outfile)
209 {
210 	if (outfile == stdout) {
211 		if (fflush(stdout))
212 			error(EXIT_FAILURE, errno, "close_outfile: fflush");
213 	} else {
214 		if (close_stream(outfile))
215 			error(EXIT_FAILURE, errno, "close_outfile: fclose");
216 	}
217 }
218 
219 /*! \brief Text output format, which is the default. */
output_txt(struct conf_t * state)220 static int output_txt(struct conf_t *state)
221 {
222 	struct range_t *range_p;
223 	struct shared_network_t *shared_p;
224 	struct output_helper_t oh;
225 	FILE *outfile;
226 	int max_ipaddr_length = state->ip_version == IPv6 ? 39 : 16;
227 
228 	if (state->color_mode == color_auto && isatty(STDIN_FILENO)) {
229 		state->color_mode = color_on;
230 	}
231 
232 	outfile = open_outfile(state);
233 	range_p = state->ranges;
234 
235 	if (state->header_limit & R_BIT) {
236 		fprintf(outfile, "Ranges:\n");
237 		fprintf
238 		    (outfile,
239 		     "%-20s%-*s   %-*s %5s %5s %10s  %5s %5s %9s",
240 		     "shared net name",
241 		     max_ipaddr_length,
242 		     "first ip",
243 		     max_ipaddr_length,
244 		     "last ip", "max", "cur", "percent", "touch", "t+c", "t+c perc");
245 		if (state->backups_found == 1) {
246 			fprintf(outfile, "     bu  bu perc");
247 		}
248 		fprintf(outfile, "\n");
249 	}
250 	if (state->number_limit & R_BIT) {
251 		unsigned int i;
252 		for (i = 0; i < state->num_ranges; i++) {
253 			int color_set = 0;
254 
255 			if (range_output_helper(state, &oh, range_p)) {
256 				range_p++;
257 				continue;
258 			}
259 			if (state->color_mode == color_on)
260 				color_set = start_color(state, &oh, outfile);
261 			if (range_p->shared_net) {
262 				fprintf(outfile, "%-20s", range_p->shared_net->name);
263 			} else {
264 				fprintf(outfile, "not_defined         ");
265 			}
266 			/* Outputting of first_ip and last_ip need to be
267 			 * separate since ntop_ipaddr always returns the
268 			 * same buffer */
269 			fprintf(outfile, "%-*s",
270 				max_ipaddr_length, ntop_ipaddr(&range_p->first_ip));
271 			fprintf(outfile,
272 				" - %-*s %5g %5g %10.3f  %5g %5g %9.3f",
273 				max_ipaddr_length,
274 				ntop_ipaddr(&range_p->last_ip),
275 				oh.range_size,
276 				range_p->count,
277 				oh.percent,
278 				range_p->touched,
279 				oh.tc,
280 				oh.tcp);
281 			if (state->backups_found == 1) {
282 				fprintf(outfile, "%7g %8.3f", range_p->backups, oh.bup);
283 			}
284 			if (color_set)
285 				fputs(color_tags[COLOR_RESET][state->color_format], outfile);
286 			fprintf(outfile, "\n");
287 			range_p++;
288 		}
289 	}
290 	if (state->number_limit & R_BIT && state->header_limit & S_BIT) {
291 		fprintf(outfile, "\n");
292 	}
293 	if (state->header_limit & S_BIT) {
294 		fprintf(outfile, "Shared networks:\n");
295 		fprintf(outfile,
296 			"name                   max   cur     percent  touch    t+c  t+c perc");
297 		if (state->backups_found == 1) {
298 			fprintf(outfile, "     bu  bu perc");
299 		}
300 		fprintf(outfile, "\n");
301 	}
302 	if (state->number_limit & S_BIT) {
303 		for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) {
304 			int color_set = 0;
305 
306 			if (shnet_output_helper(state, &oh, shared_p))
307 				continue;
308 			if (state->color_mode == color_on)
309 				color_set = start_color(state, &oh, outfile);
310 			fprintf(outfile,
311 				"%-20s %5g %5g %10.3f %7g %6g %9.3f",
312 				shared_p->name,
313 				shared_p->available,
314 				shared_p->used,
315 				oh.percent,
316 				shared_p->touched,
317 				oh.tc,
318 				oh.tcp);
319 			if (state->backups_found == 1) {
320 				fprintf(outfile, "%7g %8.3f", shared_p->backups, oh.bup);
321 			}
322 			if (color_set)
323 				fputs(color_tags[COLOR_RESET][state->color_format], outfile);
324 			fprintf(outfile, "\n");
325 		}
326 	}
327 	if (state->number_limit & S_BIT && state->header_limit & A_BIT) {
328 		fprintf(outfile, "\n");
329 	}
330 	if (state->header_limit & A_BIT) {
331 		fprintf(outfile, "Sum of all ranges:\n");
332 		fprintf(outfile,
333 			"name                   max   cur     percent  touch    t+c  t+c perc");
334 
335 		if (state->backups_found == 1) {
336 			fprintf(outfile, "     bu  bu perc");
337 		}
338 		fprintf(outfile, "\n");
339 	}
340 	if (state->number_limit & A_BIT) {
341 		int color_set = 0;
342 
343 		shnet_output_helper(state, &oh, state->shared_net_root);
344 		if (state->color_mode == color_on)
345 			color_set = start_color(state, &oh, outfile);
346 		fprintf(outfile, "%-20s %5g %5g %10.3f %7g %6g %9.3f",
347 			state->shared_net_root->name,
348 			state->shared_net_root->available,
349 			state->shared_net_root->used,
350 			oh.percent,
351 			state->shared_net_root->touched,
352 			oh.tc,
353 			oh.tcp);
354 
355 		if (state->backups_found == 1) {
356 			fprintf(outfile, "%7g %8.3f", state->shared_net_root->backups, oh.bup);
357 		}
358 		if (color_set)
359 			fputs(color_tags[COLOR_RESET][state->color_format], outfile);
360 		fprintf(outfile, "\n");
361 	}
362 	close_outfile(outfile);
363 	return 0;
364 }
365 
366 /*! \brief The xml output formats. */
output_xml(struct conf_t * state)367 static int output_xml(struct conf_t *state)
368 {
369 	struct range_t *range_p;
370 	struct shared_network_t *shared_p;
371 	struct output_helper_t oh;
372 	FILE *outfile;
373 
374 	outfile = open_outfile(state);
375 	range_p = state->ranges;
376 
377 	fprintf(outfile, "<dhcpstatus>\n");
378 
379 	if (state->print_mac_addreses) {
380 		struct leases_t *l;
381 
382 		for (l = state->leases; l != NULL; l = l->hh.next) {
383 			if (l->type == ACTIVE) {
384 				fputs("<active_lease>\n\t<ip>", outfile);
385 				fputs(ntop_ipaddr(&l->ip), outfile);
386 				fputs("</ip>\n\t<macaddress>", outfile);
387 				if (l->ethernet != NULL) {
388 					fputs(l->ethernet, outfile);
389 				}
390 				fputs("</macaddress>\n</active_lease>\n", outfile);
391 			}
392 		}
393 	}
394 
395 	if (state->number_limit & R_BIT) {
396 		unsigned int i;
397 		for (i = 0; i < state->num_ranges; i++) {
398 			if (range_output_helper(state, &oh, range_p)) {
399 				range_p++;
400 				continue;
401 			}
402 			fprintf(outfile, "<subnet>\n");
403 			if (range_p->shared_net) {
404 				fprintf(outfile,
405 					"\t<location>%s</location>\n", range_p->shared_net->name);
406 			} else {
407 				fprintf(outfile, "\t<location></location>\n");
408 			}
409 			fprintf(outfile, "\t<range>%s ", ntop_ipaddr(&range_p->first_ip));
410 			fprintf(outfile, "- %s</range>\n", ntop_ipaddr(&range_p->last_ip));
411 			fprintf(outfile, "\t<defined>%g</defined>\n", oh.range_size);
412 			fprintf(outfile, "\t<used>%g</used>\n", range_p->count);
413 			fprintf(outfile, "\t<touched>%g</touched>\n", range_p->touched);
414 			fprintf(outfile, "\t<free>%g</free>\n", oh.range_size - range_p->count);
415 			range_p++;
416 			fprintf(outfile, "</subnet>\n");
417 		}
418 	}
419 
420 	if (state->number_limit & S_BIT) {
421 		for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) {
422 			if (shnet_output_helper(state, &oh, shared_p))
423 				continue;
424 			fprintf(outfile, "<shared-network>\n");
425 			fprintf(outfile, "\t<location>%s</location>\n", shared_p->name);
426 			fprintf(outfile, "\t<defined>%g</defined>\n", shared_p->available);
427 			fprintf(outfile, "\t<used>%g</used>\n", shared_p->used);
428 			fprintf(outfile, "\t<touched>%g</touched>\n", shared_p->touched);
429 			fprintf(outfile, "\t<free>%g</free>\n",
430 				shared_p->available - shared_p->used);
431 			fprintf(outfile, "</shared-network>\n");
432 		}
433 	}
434 
435 	if (state->header_limit & A_BIT) {
436 		fprintf(outfile, "<summary>\n");
437 		fprintf(outfile, "\t<location>%s</location>\n", state->shared_net_root->name);
438 		fprintf(outfile, "\t<defined>%g</defined>\n", state->shared_net_root->available);
439 		fprintf(outfile, "\t<used>%g</used>\n", state->shared_net_root->used);
440 		fprintf(outfile, "\t<touched>%g</touched>\n", state->shared_net_root->touched);
441 		fprintf(outfile, "\t<free>%g</free>\n",
442 			state->shared_net_root->available - state->shared_net_root->used);
443 		fprintf(outfile, "</summary>\n");
444 	}
445 
446 	fprintf(outfile, "</dhcpstatus>\n");
447 	close_outfile(outfile);
448 	return 0;
449 }
450 
451 /*! \brief The json output formats. */
output_json(struct conf_t * state)452 static int output_json(struct conf_t *state)
453 {
454 	unsigned int i = 0;
455 	struct range_t *range_p;
456 	struct shared_network_t *shared_p;
457 	struct output_helper_t oh;
458 	FILE *outfile;
459 	unsigned int sep;
460 
461 	outfile = open_outfile(state);
462 	range_p = state->ranges;
463 	sep = 0;
464 
465 	fprintf(outfile, "{\n");
466 
467 	if (state->print_mac_addreses) {
468 		struct leases_t *l;
469 
470 		fprintf(outfile, "   \"active_leases\": [");
471 		for (l = state->leases; l != NULL; l = l->hh.next) {
472 			if (l->type == ACTIVE) {
473 				if (i == 0) {
474 					i = 1;
475 				} else {
476 					fputc(',', outfile);
477 				}
478 				fputs("\n         { \"ip\":\"", outfile);
479 				fputs(ntop_ipaddr(&l->ip), outfile);
480 				fputs("\", \"macaddress\":\"", outfile);
481 				if (l->ethernet != NULL) {
482 					fputs(l->ethernet, outfile);
483 				}
484 				fputs("\" }", outfile);
485 			}
486 		}
487 		fprintf(outfile, "\n   ]");	/* end of active_leases */
488 		sep++;
489 	}
490 
491 	if (state->number_limit & R_BIT) {
492 		if (sep) {
493 			fprintf(outfile, ",\n");
494 		}
495 		fprintf(outfile, "   \"subnets\": [\n");
496 		for (i = 0; i < state->num_ranges; i++) {
497 			if (range_output_helper(state, &oh, range_p)) {
498 				range_p++;
499 				continue;
500 			}
501 			fprintf(outfile, "         ");
502 			fprintf(outfile, "{ ");
503 			if (range_p->shared_net) {
504 				fprintf(outfile,
505 					"\"location\":\"%s\", ", range_p->shared_net->name);
506 			} else {
507 				fprintf(outfile, "\"location\":\"\", ");
508 			}
509 
510 			fprintf(outfile, "\"range\":\"%s", ntop_ipaddr(&range_p->first_ip));
511 			fprintf(outfile, " - %s\", ", ntop_ipaddr(&range_p->last_ip));
512 			fprintf(outfile, "\"first_ip\":\"%s\", ", ntop_ipaddr(&range_p->first_ip));
513 			fprintf(outfile, "\"last_ip\":\"%s\", ", ntop_ipaddr(&range_p->last_ip));
514 			fprintf(outfile, "\"defined\":%g, ", oh.range_size);
515 			fprintf(outfile, "\"used\":%g, ", range_p->count);
516 			fprintf(outfile, "\"touched\":%g, ", range_p->touched);
517 			fprintf(outfile, "\"free\":%g, ", oh.range_size - range_p->count);
518 			fprintf(outfile, "\"percent\":%g, ", oh.percent);
519 			fprintf(outfile, "\"touch_count\":%g, ", oh.tc);
520 			fprintf(outfile, "\"touch_percent\":%g, ", oh.tcp);
521 			if (state->backups_found == 1) {
522 				fprintf(outfile, "\"backup_count\":%g, ", range_p->backups);
523 				fprintf(outfile, "\"backup_percent\":%g, ", oh.bup);
524 			}
525 			fprintf(outfile, "\"status\":%d ", oh.status);
526 
527 			range_p++;
528 			if (i + 1 < state->num_ranges)
529 				fprintf(outfile, "},\n");
530 			else
531 				fprintf(outfile, "}\n");
532 		}
533 		fprintf(outfile, "   ]");	/* end of subnets */
534 		sep++;
535 	}
536 
537 	if (state->number_limit & S_BIT) {
538 		if (sep) {
539 			fprintf(outfile, ",\n");
540 		}
541 		fprintf(outfile, "   \"shared-networks\": [\n");
542 		for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) {
543 			if (shnet_output_helper(state, &oh, shared_p))
544 				continue;
545 			fprintf(outfile, "         ");
546 			fprintf(outfile, "{ ");
547 			fprintf(outfile, "\"location\":\"%s\", ", shared_p->name);
548 			fprintf(outfile, "\"defined\":%g, ", shared_p->available);
549 			fprintf(outfile, "\"used\":%g, ", shared_p->used);
550 			fprintf(outfile, "\"touched\":%g, ", shared_p->touched);
551 			fprintf(outfile, "\"free\":%g, ", shared_p->available - shared_p->used);
552 			if (fpclassify(shared_p->available) == FP_ZERO)
553 				fprintf(outfile, "\"percent\":\"%g\", ", oh.percent);
554 			else
555 				fprintf(outfile, "\"percent\":%g, ", oh.percent);
556 			fprintf(outfile, "\"touch_count\":%g, ", oh.tc);
557 			if (fpclassify(shared_p->available) == FP_ZERO)
558 				fprintf(outfile, "\"touch_percent\":\"%g\", ", oh.tcp);
559 			else
560 				fprintf(outfile, "\"touch_percent\":%g, ", oh.tcp);
561 			if (state->backups_found == 1) {
562 				fprintf(outfile, "\"backup_count\":%g, ", shared_p->backups);
563 				if (fpclassify(shared_p->available) == FP_ZERO)
564 					fprintf(outfile, "\"backup_percent\":\"%g\", ", oh.bup);
565 				else
566 					fprintf(outfile, "\"backup_percent\":%g, ", oh.bup);
567 			}
568 			fprintf(outfile, "\"status\":%d ", oh.status);
569 			if (shared_p->next)
570 				fprintf(outfile, "},\n");
571 			else
572 				fprintf(outfile, "}\n");
573 		}
574 		fprintf(outfile, "   ]");	/* end of shared-networks */
575 		sep++;
576 	}
577 
578 	if (state->header_limit & A_BIT) {
579 		shnet_output_helper(state, &oh, state->shared_net_root);
580 		if (sep) {
581 			fprintf(outfile, ",\n");
582 		}
583 		fprintf(outfile, "   \"summary\": {\n");
584 		fprintf(outfile, "         \"location\":\"%s\",\n", state->shared_net_root->name);
585 		fprintf(outfile, "         \"defined\":%g,\n", state->shared_net_root->available);
586 		fprintf(outfile, "         \"used\":%g,\n", state->shared_net_root->used);
587 		fprintf(outfile, "         \"touched\":%g,\n", state->shared_net_root->touched);
588 		fprintf(outfile, "         \"free\":%g,\n",
589 			state->shared_net_root->available - state->shared_net_root->used);
590 		fprintf(outfile, "         \"percent\":%g,\n", oh.percent);
591 		fprintf(outfile, "         \"touch_count\":%g,\n", oh.tc);
592 		fprintf(outfile, "         \"touch_percent\":%g,\n", oh.tcp);
593 		if (state->backups_found == 1) {
594 			fprintf(outfile, "         \"backup_count\":%g,\n",
595 				state->shared_net_root->backups);
596 			fprintf(outfile, "         \"backup_percent\":%g,\n", oh.bup);
597 		}
598 		fprintf(outfile, "         \"status\":%d\n", oh.status);
599 		fprintf(outfile, "   },\n");	/* end of summary */
600 		fprintf(outfile, "   \"trivia\": {\n");
601 		fprintf(outfile, "         \"version\":\"%s\",\n", PACKAGE_VERSION);
602 		fprintf(outfile, "         \"conf_file_path\":\"%s\",\n", state->dhcpdconf_file);
603 		fprintf(outfile, "         \"conf_file_epoch_mtime\":");
604 		dp_time_tool(outfile, state->dhcpdconf_file, 1);
605 		fprintf(outfile, ",\n");
606 		fprintf(outfile, "         \"lease_file_path\":\"%s\",\n", state->dhcpdlease_file);
607 		fprintf(outfile, "         \"lease_file_epoch_mtime\":");
608 		dp_time_tool(outfile, state->dhcpdlease_file, 1);
609 		fprintf(outfile, "\n");
610 
611 		fprintf(outfile, "   }");	/* end of trivia */
612 	}
613 	fprintf(outfile, "\n}\n");
614 	close_outfile(outfile);
615 	return 0;
616 }
617 
618 /*! \brief Header for full html output format.
619  *
620  * \param f Output file descriptor.
621  */
html_header(struct conf_t * state,FILE * restrict f)622 static void html_header(struct conf_t *state, FILE *restrict f)
623 {
624 	fprintf(f, "<!DOCTYPE html>\n");
625 	fprintf(f, "<html>\n");
626 	fprintf(f, "<head>\n");
627 	fprintf(f, "<title>ISC dhcpd dhcpd-pools output</title>\n");
628 	fprintf(f, "<meta charset=\"utf-8\">\n");
629 	fprintf(f, "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n");
630 	fprintf(f, "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n");
631 	fprintf(f, "<link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css\" type=\"text/css\">\n");
632 	fprintf(f, "<link rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css\">\n");
633 	fprintf(f, "<style type=\"text/css\">\n");
634 	fprintf(f, "table.dhcpd-pools th { text-transform: capitalize }\n");
635 	fprintf(f, "</style>\n");
636 	fprintf(f, "</head>\n");
637 	fprintf(f, "<body>\n");
638 	fprintf(f, "<div class=\"container\">\n");
639 	fprintf(f, "<h2>ISC DHCPD status</h2>\n");
640 	fprintf(f, "<small>File %s was last modified at ", state->dhcpdlease_file);
641 	dp_time_tool(f, state->dhcpdlease_file, 0);
642 	fprintf(f, "</small><hr />\n");
643 }
644 
645 /*! \brief Footer for full html output format.
646  *
647  * \param f Output file descriptor.
648  */
html_footer(FILE * restrict f)649 static void html_footer(FILE *restrict f)
650 {
651 	fprintf(f, "<br /><div class=\"well well-lg\">\n");
652 	fprintf(f, "<small>Generated using %s<br />\n", PACKAGE_STRING);
653 	fprintf(f, "More info at <a href=\"%s\">%s</a>\n", PACKAGE_URL, PACKAGE_URL);
654 	fprintf(f, "</small></div></div>\n");
655 	fprintf(f, "<script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js\" type=\"text/javascript\"></script>\n");
656 	fprintf(f, "<script type=\"text/javascript\" src=\"https://cdn.datatables.net/v/bs/jq-3.2.1/dt-1.10.16/datatables.min.js\"></script>\n");
657 	fprintf(f, "<script type=\"text/javascript\" class=\"init\">$(document).ready(function() { $('#s').DataTable({ \"iDisplayLength\": 50, \"lengthMenu\": [ [25, 50, 100, -1], [25, 50, 100, \"All\"] ], \"order\": [[ 4, \"desc\" ]] } ); } );</script>\n");
658 	fprintf(f, "<script type=\"text/javascript\" class=\"init\">$(document).ready(function() { $('#r').DataTable({ \"iDisplayLength\": 100, \"lengthMenu\": [ [25, 50, 100, -1], [25, 50, 100, \"All\"] ], \"order\": [[ 6, \"desc\" ]] } ); } );</script>\n");
659 	fprintf(f, "</body></html>\n");
660 }
661 
662 /*! \brief Start a html tag.
663  *
664  * \param f Output file descriptor.
665  * \param tag The html tag.
666  */
start_tag(FILE * restrict f,char const * restrict tag)667 static void start_tag(FILE *restrict f, char const *restrict tag)
668 {
669 	fprintf(f, "<%s>\n", tag);
670 }
671 
672 /*! \brief End a html tag.
673  *
674  * \param f Output file descriptor.
675  * \param tag The html tag.
676  */
end_tag(FILE * restrict f,char const * restrict tag)677 static void end_tag(FILE *restrict f, char const *restrict tag)
678 {
679 	fprintf(f, "</%s>\n", tag);
680 }
681 
682 /*! \brief Line with text in html output format.
683  *
684  * \param f Output file descriptor.
685  * \param type HTML tag name.
686  * \param class How the data is aligned.
687  * \param text Actual payload of the printout.
688  */
output_line(FILE * restrict f,char const * restrict type,char const * restrict text)689 static void output_line(FILE *restrict f, char const *restrict type, char const *restrict text)
690 {
691 	fprintf(f, "<%s>%s</%s>\n", type, text, type);
692 }
693 
694 /*! \brief Line with digit in html output format.
695  *
696  * \param f Output file descriptor.
697  * \param type HMTL tag name.
698  * \param d Actual payload of the printout.
699  */
output_double(FILE * restrict f,char const * restrict type,double d)700 static void output_double(FILE *restrict f, char const *restrict type, double d)
701 {
702 	fprintf(f, "<%s>%g</%s>\n", type, d, type);
703 }
704 
705 /*! \brief Line with a potentially colored digit in html output format.
706  *
707  * \param state Runtime configuration state.
708  * \param f Output file descriptor.
709  * \param type HMTL tag name.
710  * \param d Actual payload of the printout.
711  */
output_double_color(struct conf_t * state,struct output_helper_t * oh,FILE * restrict f,char const * restrict type)712 static void output_double_color(struct conf_t *state,
713 				struct output_helper_t *oh, FILE *restrict f,
714 				char const *restrict type)
715 {
716 	fprintf(f, "<%s", type);
717 	if (state->color_mode == color_on)
718 		start_color(state, oh, f);
719 	fprintf(f, ">%g", oh->percent);
720 	fprintf(f, "</%s>\n", type);
721 }
722 
723 /*! \brief Line with float in html output format.
724  *
725  * \param f Output file descriptor.
726  * \param type HTML tag name.
727  * \param fl Actual payload of the printout.
728  */
output_float(FILE * restrict f,char const * restrict type,double fl)729 static void output_float(FILE *restrict f, char const *restrict type, double fl)
730 {
731 	fprintf(f, "<%s>%.3f</%s>\n", type, fl, type);
732 }
733 
734 /*! \brief Begin table in html output format.
735  *
736  * \param f Output file descriptor.
737  */
table_start(FILE * restrict f,char const * restrict id,char const * restrict summary)738 static void table_start(FILE *restrict f, char const *restrict id, char const *restrict summary)
739 {
740 	fprintf(f, "<table id=\"%s\" class=\"dhcpd-pools order-column table table-hover\" summary=\"%s\">\n", id, summary);
741 }
742 
743 /*! \brief End table in html output format.
744  *
745  * \param f Output file descriptor.
746  */
table_end(FILE * restrict f)747 static void table_end(FILE *restrict f)
748 {
749 	fprintf(f, "</table>\n");
750 }
751 
752 /*! \brief New section in html output format.
753  *
754  * \param f Output file descriptor.
755  * \param title Table title.
756  */
newsection(FILE * restrict f,char const * restrict title)757 static void newsection(FILE *restrict f, char const *restrict title)
758 {
759 	output_line(f, "h3", title);
760 }
761 
762 /*! \brief Output html format. */
output_html(struct conf_t * state)763 static int output_html(struct conf_t *state)
764 {
765 	struct range_t *range_p;
766 	struct shared_network_t *shared_p;
767 	struct output_helper_t oh;
768 	FILE *outfile;
769 
770 	outfile = open_outfile(state);
771 	range_p = state->ranges;
772 	html_header(state, outfile);
773 	newsection(outfile, "Sum of all");
774 	table_start(outfile, "a", "all");
775 	if (state->header_limit & A_BIT) {
776 		start_tag(outfile, "thead");
777 		start_tag(outfile, "tr");
778 		output_line(outfile, "th", "name");
779 		output_line(outfile, "th", "max");
780 		output_line(outfile, "th", "cur");
781 		output_line(outfile, "th", "free");
782 		output_line(outfile, "th", "percent");
783 		output_line(outfile, "th", "touch");
784 		output_line(outfile, "th", "t+c");
785 		output_line(outfile, "th", "t+c perc");
786 		if (state->backups_found == 1) {
787 			output_line(outfile, "th", "bu");
788 			output_line(outfile, "th", "bu perc");
789 		}
790 		end_tag(outfile, "tr");
791 		end_tag(outfile, "thead");
792 	}
793 	if (state->number_limit & A_BIT) {
794 		start_tag(outfile, "tbody");
795 		start_tag(outfile, "tr");
796 		shnet_output_helper(state, &oh, state->shared_net_root);
797 		output_line(outfile, "td", state->shared_net_root->name);
798 		output_double(outfile, "td", state->shared_net_root->available);
799 		output_double(outfile, "td", state->shared_net_root->used);
800 		output_double(outfile, "td", state->shared_net_root->available - state->shared_net_root->used);
801 		output_float(outfile, "td", oh.percent);
802 		output_double(outfile, "td", state->shared_net_root->touched);
803 		output_double(outfile, "td", oh.tc);
804 		output_float(outfile, "td", oh.tcp);
805 		if (state->backups_found == 1) {
806 			output_double(outfile, "td", state->shared_net_root->backups);
807 			output_float(outfile, "td", oh.tcp);
808 		}
809 		end_tag(outfile, "tr");
810 		end_tag(outfile, "tbody");
811 	}
812 	table_end(outfile);
813 	newsection(outfile, "Shared networks");
814 	table_start(outfile, "s", "snet");
815 	if (state->header_limit & S_BIT) {
816 		start_tag(outfile, "thead");
817 		start_tag(outfile, "tr");
818 		output_line(outfile, "th", "name");
819 		output_line(outfile, "th", "max");
820 		output_line(outfile, "th", "cur");
821 		output_line(outfile, "th", "free");
822 		output_line(outfile, "th", "percent");
823 		output_line(outfile, "th", "touch");
824 		output_line(outfile, "th", "t+c");
825 		output_line(outfile, "th", "t+c perc");
826 		if (state->backups_found == 1) {
827 			output_line(outfile, "th", "bu");
828 			output_line(outfile, "th", "bu perc");
829 		}
830 		end_tag(outfile, "tr");
831 		end_tag(outfile, "thead");
832 	}
833 	if (state->number_limit & S_BIT) {
834 		start_tag(outfile, "tbody");
835 		for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) {
836 			if (shnet_output_helper(state, &oh, shared_p))
837 				continue;
838 			start_tag(outfile, "tr");
839 			output_line(outfile, "td", shared_p->name);
840 			output_double(outfile, "td", shared_p->available);
841 			output_double(outfile, "td", shared_p->used);
842 			output_double(outfile, "td", shared_p->available - shared_p->used);
843 			output_double_color(state, &oh, outfile, "td");
844 			output_double(outfile, "td", shared_p->touched);
845 			output_double(outfile, "td", oh.tc);
846 			output_float(outfile, "td", oh.tcp);
847 			if (state->backups_found == 1) {
848 				output_double(outfile, "td", shared_p->backups);
849 				output_float(outfile, "td", oh.bup);
850 			}
851 			end_tag(outfile, "tr");
852 		}
853 		end_tag(outfile, "tbody");
854 	}
855 	table_end(outfile);
856 	newsection(outfile, "Ranges");
857 	table_start(outfile, "r", "ranges");
858 	if (state->header_limit & R_BIT) {
859 		start_tag(outfile, "thead");
860 		start_tag(outfile, "tr");
861 		output_line(outfile, "th", "shared net name");
862 		output_line(outfile, "th", "first ip");
863 		output_line(outfile, "th", "last ip");
864 		output_line(outfile, "th", "max");
865 		output_line(outfile, "th", "cur");
866 		output_line(outfile, "th", "free");
867 		output_line(outfile, "th", "percent");
868 		output_line(outfile, "th", "touch");
869 		output_line(outfile, "th", "t+c");
870 		output_line(outfile, "th", "t+c perc");
871 		if (state->backups_found == 1) {
872 			output_line(outfile, "th", "bu");
873 			output_line(outfile, "th", "bu perc");
874 		}
875 		end_tag(outfile, "tr");
876 		end_tag(outfile, "thead");
877 	}
878 	if (state->number_limit & R_BIT) {
879 		unsigned int i;
880 		start_tag(outfile, "tbody");
881 		for (i = 0; i < state->num_ranges; i++) {
882 			if (range_output_helper(state, &oh, range_p)) {
883 				range_p++;
884 				continue;
885 			}
886 			start_tag(outfile, "tr");
887 			if (range_p->shared_net) {
888 				output_line(outfile, "td", range_p->shared_net->name);
889 			} else {
890 				output_line(outfile, "td", "not_defined");
891 			}
892 			output_line(outfile, "td", ntop_ipaddr(&range_p->first_ip));
893 			output_line(outfile, "td", ntop_ipaddr(&range_p->last_ip));
894 			output_double(outfile, "td", oh.range_size);
895 			output_double(outfile, "td", range_p->count);
896 			output_double(outfile, "td", oh.range_size - range_p->count);
897 			output_double_color(state, &oh, outfile, "td");
898 			output_double(outfile, "td", range_p->touched);
899 			output_double(outfile, "td", oh.tc);
900 			output_float(outfile, "td", oh.tcp);
901 			if (state->backups_found == 1) {
902 				output_double(outfile, "td", range_p->backups);
903 				output_float(outfile, "td", oh.bup);
904 			}
905 			end_tag(outfile, "tr");
906 			range_p++;
907 		}
908 		end_tag(outfile, "tbody");
909 	}
910 	table_end(outfile);
911 	html_footer(outfile);
912 	close_outfile(outfile);
913 	return 0;
914 }
915 
916 /*! \brief Output cvs format. */
output_csv(struct conf_t * state)917 static int output_csv(struct conf_t *state)
918 {
919 	struct range_t *range_p;
920 	struct shared_network_t *shared_p;
921 	struct output_helper_t oh;
922 	FILE *outfile;
923 
924 	outfile = open_outfile(state);
925 	range_p = state->ranges;
926 	if (state->header_limit & R_BIT) {
927 		fprintf(outfile, "\"Ranges:\"\n");
928 		fprintf
929 		    (outfile,
930 		     "\"shared net name\",\"first ip\",\"last ip\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\"");
931 		if (state->backups_found == 1) {
932 			fprintf(outfile, ",\"bu\",\"bu perc\"");
933 		}
934 		fprintf(outfile, "\n");
935 	}
936 	if (state->number_limit & R_BIT) {
937 		unsigned int i;
938 		for (i = 0; i < state->num_ranges; i++) {
939 			if (range_output_helper(state, &oh, range_p)) {
940 				range_p++;
941 				continue;
942 			}
943 			if (range_p->shared_net) {
944 				fprintf(outfile, "\"%s\",", range_p->shared_net->name);
945 			} else {
946 				fprintf(outfile, "\"not_defined\",");
947 			}
948 			fprintf(outfile, "\"%s\",", ntop_ipaddr(&range_p->first_ip));
949 			fprintf(outfile,
950 				"\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"",
951 				ntop_ipaddr(&range_p->last_ip),
952 				oh.range_size,
953 				range_p->count,
954 				oh.percent,
955 				range_p->touched,
956 				oh.tc,
957 				oh.tcp);
958 			if (state->backups_found == 1) {
959 				fprintf(outfile, ",\"%g\",\"%.3f\"", range_p->backups, oh.bup);
960 			}
961 
962 			fprintf(outfile, "\n");
963 			range_p++;
964 		}
965 		fprintf(outfile, "\n");
966 	}
967 	if (state->header_limit & S_BIT) {
968 		fprintf(outfile, "\"Shared networks:\"\n");
969 		fprintf(outfile,
970 			"\"name\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\"");
971 		if (state->backups_found == 1) {
972 			fprintf(outfile, ",\"bu\",\"bu perc\"");
973 		}
974 		fprintf(outfile, "\n");
975 	}
976 	if (state->number_limit & S_BIT) {
977 
978 		for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) {
979 			if (shnet_output_helper(state, &oh, shared_p))
980 				continue;
981 			fprintf(outfile,
982 				"\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"",
983 				shared_p->name,
984 				shared_p->available,
985 				shared_p->used,
986 				oh.percent,
987 				shared_p->touched,
988 				oh.tc,
989 				oh.tcp);
990 			if (state->backups_found == 1) {
991 				fprintf(outfile, ",\"%g\",\"%.3f\"", shared_p->backups, oh.bup);
992 			}
993 
994 			fprintf(outfile, "\n");
995 		}
996 		fprintf(outfile, "\n");
997 	}
998 	if (state->header_limit & A_BIT) {
999 		fprintf(outfile, "\"Sum of all ranges:\"\n");
1000 		fprintf(outfile,
1001 			"\"name\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\"");
1002 		if (state->backups_found == 1) {
1003 			fprintf(outfile, ",\"bu\",\"bu perc\"");
1004 		}
1005 		fprintf(outfile, "\n");
1006 	}
1007 	if (state->number_limit & A_BIT) {
1008 		shnet_output_helper(state, &oh, state->shared_net_root);
1009 		fprintf(outfile,
1010 			"\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"",
1011 			state->shared_net_root->name,
1012 			state->shared_net_root->available,
1013 			state->shared_net_root->used,
1014 			oh.percent,
1015 			state->shared_net_root->touched,
1016 			oh.tc,
1017 			oh.tcp);
1018 		if (state->backups_found == 1) {
1019 			fprintf(outfile, "%7g %8.3f", state->shared_net_root->backups, oh.bup);
1020 		}
1021 		fprintf(outfile, "\n");
1022 	}
1023 	close_outfile(outfile);
1024 	return 0;
1025 }
1026 
range_alarms(struct conf_t * state,struct status_counts_t * rangstat)1027 void range_alarms(struct conf_t *state, struct status_counts_t *rangstat)
1028 {
1029 	struct output_helper_t oh;
1030 	struct range_t *range_p = state->ranges;
1031 	unsigned int i;
1032 
1033 	if (state->number_limit & R_BIT) {
1034 		for (i = 0; i < state->num_ranges; i++) {
1035 			range_output_helper(state, &oh, range_p);
1036 			switch (oh.status) {
1037 			case STATUS_SUPPRESSED:
1038 				break;
1039 			case STATUS_IGNORED:
1040 				rangstat->ignored++;
1041 				break;
1042 			case STATUS_CRIT:
1043 				rangstat->critical++;
1044 				break;
1045 			case STATUS_WARN:
1046 				rangstat->warning++;
1047 				break;
1048 			case STATUS_OK:
1049 				rangstat->ok++;
1050 				break;
1051 			default:
1052 				abort();
1053 			}
1054 			range_p++;
1055 		}
1056 	}
1057 }
1058 
shared_net_alarms(struct conf_t * state,struct status_counts_t * sharstat)1059 void shared_net_alarms(struct conf_t *state, struct status_counts_t *sharstat)
1060 {
1061 	struct output_helper_t oh;
1062 	struct shared_network_t *shared_p;
1063 
1064 	if (state->number_limit & S_BIT) {
1065 		for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) {
1066 			shnet_output_helper(state, &oh, shared_p);
1067 			switch (oh.status) {
1068 			case STATUS_SUPPRESSED:
1069 				break;
1070 			case STATUS_IGNORED:
1071 				sharstat->ignored++;
1072 				break;
1073 			case STATUS_CRIT:
1074 				sharstat->critical++;
1075 				break;
1076 			case STATUS_WARN:
1077 				sharstat->warning++;
1078 				break;
1079 			case STATUS_OK:
1080 				sharstat->ok++;
1081 				break;
1082 			default:
1083 				abort();
1084 			}
1085 		}
1086 	}
1087 }
1088 
1089 
1090 /*! \brief Output alarm text, and return program exit value. */
output_alarming(struct conf_t * state)1091 static int output_alarming(struct conf_t *state)
1092 {
1093 	FILE *outfile;
1094 	unsigned int i;
1095 	int ret_val;
1096 	struct status_counts_t rangstat = { 0 };
1097 	struct status_counts_t sharstat = { 0 };
1098 
1099 	outfile = open_outfile(state);
1100 
1101 	range_alarms(state, &rangstat);
1102 	shared_net_alarms(state, &sharstat);
1103 
1104 	if (rangstat.critical || sharstat.critical)
1105 		ret_val = STATE_CRITICAL;
1106 	else if (rangstat.warning || sharstat.warning)
1107 		ret_val = STATE_WARNING;
1108 	else
1109 		ret_val = STATE_OK;
1110 
1111 	if ((0 < rangstat.critical && state->number_limit & R_BIT)
1112 	    || (0 < sharstat.critical && state->number_limit & S_BIT)) {
1113 		fprintf(outfile, "CRITICAL: %s:", program_name);
1114 	} else if ((0 < rangstat.warning && state->number_limit & R_BIT)
1115 		   || (0 < sharstat.warning && state->number_limit & S_BIT)) {
1116 		fprintf(outfile, "WARNING: %s:", program_name);
1117 	} else {
1118 		if (state->number_limit & A_BIT)
1119 			fprintf(outfile, "OK:");
1120 		else {
1121 			if (close_stream(outfile)) {
1122 				error(EXIT_FAILURE, errno, "output_alarming: fclose");
1123 			}
1124 			return ret_val;
1125 		}
1126 	}
1127 	if (state->header_limit & R_BIT) {
1128 		fprintf(outfile, " Ranges - crit: %d warn: %d ok: %d", rangstat.critical, rangstat.warning, rangstat.ok);
1129 		if (rangstat.ignored != 0) {
1130 			fprintf(outfile, " ignored: %d", rangstat.ignored);
1131 		}
1132 		fprintf(outfile, "; | range_crit=%d range_warn=%d range_ok=%d", rangstat.critical, rangstat.warning, rangstat.ok);
1133 		if (rangstat.ignored != 0) {
1134 			fprintf(outfile, " range_ignored=%d", rangstat.ignored);
1135 		}
1136 		if (state->perfdata == 1 && state->number_limit & R_BIT) {
1137 			/* last to first order. too late to fix this after so many years */
1138 			struct range_t *range_p = state->ranges + state->num_ranges - 1;
1139 
1140 			for (i = state->num_ranges; i; i--) {
1141 				struct output_helper_t oh;
1142 
1143 				if (range_output_helper(state, &oh, range_p))
1144 					continue;
1145 				if (state->minsize < oh.range_size) {
1146 					fprintf(outfile, " %s_r=", ntop_ipaddr(&range_p->first_ip));
1147 					fprintf(outfile, "%g;%g;%g;0;%g",
1148 						range_p->count,
1149 						(oh.range_size * state->warning / 100),
1150 						(oh.range_size * state->critical / 100), oh.range_size);
1151 					fprintf(outfile, " %s_rt=%g",
1152 						ntop_ipaddr(&range_p->first_ip), range_p->touched);
1153 					if (state->backups_found == 1) {
1154 						fprintf(outfile, " %s_rbu=%g",
1155 							ntop_ipaddr(&range_p->first_ip),
1156 							range_p->backups);
1157 					}
1158 				}
1159 				range_p--;
1160 			}
1161 		}
1162 		fprintf(outfile, "\n");
1163 	} else {
1164 		fprintf(outfile, " ");
1165 	}
1166 	if (state->header_limit & S_BIT) {
1167 		fprintf(outfile, "Shared nets - crit: %d warn: %d ok: %d", sharstat.critical, sharstat.warning, sharstat.ok);
1168 		if (sharstat.ignored != 0) {
1169 			fprintf(outfile, " ignored: %d", sharstat.ignored);
1170 		}
1171 		fprintf(outfile, "; | snet_crit=%d snet_warn=%d snet_ok=%d", sharstat.critical, sharstat.warning, sharstat.ok);
1172 		if (sharstat.ignored != 0) {
1173 			fprintf(outfile, " snet_ignored=%d", sharstat.ignored);
1174 		}
1175 		if (state->perfdata == 1 && state->header_limit & R_BIT) {
1176 			struct shared_network_t *shared_p;
1177 
1178 			for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) {
1179 				struct output_helper_t oh;
1180 
1181 				if (shnet_output_helper(state, &oh, shared_p))
1182 					continue;
1183 				if (state->minsize < shared_p->available) {
1184 					fprintf(outfile, " '%s_s'=%g;%g;%g;0;%g",
1185 						shared_p->name,
1186 						shared_p->used,
1187 						(shared_p->available * state->warning / 100),
1188 						(shared_p->available * state->critical / 100),
1189 						shared_p->available);
1190 					fprintf(outfile, " '%s_st'=%g",
1191 						shared_p->name, shared_p->touched);
1192 					if (state->backups_found == 1) {
1193 						fprintf(outfile, " '%s_sbu'=%g",
1194 							shared_p->name, shared_p->backups);
1195 					}
1196 				}
1197 			}
1198 			fprintf(outfile, "\n");
1199 		}
1200 	}
1201 	fprintf(outfile, "\n");
1202 	close_outfile(outfile);
1203 	return ret_val;
1204 }
1205 
1206 /*! \brief Return output_format_names enum based on single char input. */
output_analysis(struct conf_t * state)1207 int output_analysis(struct conf_t *state)
1208 {
1209 	int ret = 1;
1210 
1211 	switch (state->output_format) {
1212 	case 't':
1213 		state->color_format = OUT_FORM_TEXT;
1214 		ret = output_txt(state);
1215 		break;
1216 	case 'a':
1217 		ret = output_alarming(state);
1218 		break;
1219 	case 'h':
1220 		error(EXIT_FAILURE, 0, "html table only output format is deprecated");
1221 		break;
1222 	case 'H':
1223 		state->color_format = OUT_FORM_HTML;
1224 		ret = output_html(state);
1225 		break;
1226 	case 'x':
1227 		/* fallthrough */
1228 	case 'X':
1229 		ret = output_xml(state);
1230 		break;
1231 	case 'j':
1232 		/* fallthrough */
1233 	case 'J':
1234 		ret = output_json(state);
1235 		break;
1236 	case 'c':
1237 		ret = output_csv(state);
1238 		break;
1239 #ifdef BUILD_MUSTACH
1240 	case 'm':
1241 		ret = mustach_dhcpd_pools(state);
1242 		break;
1243 #endif
1244 	default:
1245 		error(EXIT_FAILURE, 0, "unknown output format: '%c'", state->output_format);
1246 	}
1247 	return ret;
1248 }
1249