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