/* * The dhcpd-pools has BSD 2-clause license which also known as "Simplified * BSD License" or "FreeBSD License". * * Copyright 2006- Sami Kerola. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of Sami Kerola. */ /*! \file dhcpd-pools.c * \brief The main(), and core initialization. */ #include #include #include #include #include #include #include #include "close-stream.h" #include "closeout.h" #include "error.h" #include "progname.h" #include "quote.h" #include "xalloc.h" #include "dhcpd-pools.h" /* Function pointers */ int (*parse_ipaddr) (struct conf_t *state, const char *restrict src, union ipaddr_t *restrict dst); void (*copy_ipaddr) (union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); const char *(*ntop_ipaddr) (const union ipaddr_t *ip); double (*get_range_size) (const struct range_t *r); int (*xstrstr) (struct conf_t *state, const char *restrict str); int (*ipcomp) (const union ipaddr_t *restrict a, const union ipaddr_t *restrict b); int (*leasecomp) (const struct leases_t *restrict a, const struct leases_t *restrict b); void (*add_lease) (struct conf_t *state, union ipaddr_t *ip, enum ltype type); struct leases_t *(*find_lease) (struct conf_t *state, union ipaddr_t *ip); /*! \brief An option argument parser to populate state header_limit and * number_limit values. */ static int return_limit(const char c) { if ('0' <= c && c < '8') return c - '0'; error(EXIT_FAILURE, 0, "return_limit: output mask %s is illegal", quote(optarg)); return 0; } /*! \brief Run time initialization. Global allocations, counter * initializations, etc are here. */ static void prepare_memory(struct conf_t *state) { state->ranges = xmalloc(sizeof(struct range_t) * state->ranges_size); /* First shared network entry is all networks */ state->shared_net_root = xcalloc(sizeof(struct shared_network_t), 1); state->shared_net_root->name = xstrdup("All networks"); state->shared_net_head = state->shared_net_root; } /*! \brief The --skip option argument parser. */ static void skip_arg_parse(struct conf_t *state, char *arg) { enum { OPT_ARG_OK = 0, OPT_ARG_WARNING, OPT_ARG_CRITICAL, OPT_ARG_MINSIZE, OPT_ARG_SUPPRESSED }; char *const tokens[] = { [OPT_ARG_OK] = "ok", [OPT_ARG_WARNING] = "warning", [OPT_ARG_CRITICAL] = "critical", [OPT_ARG_MINSIZE] = "minsize", [OPT_ARG_SUPPRESSED] = "suppressed", NULL }; char *value; while (*arg != '\0') { switch (getsubopt(&arg, tokens, &value)) { case OPT_ARG_OK: state->skip_ok = 1; break; case OPT_ARG_WARNING: state->skip_warning = 1; break; case OPT_ARG_CRITICAL: state->skip_critical = 1; break; case OPT_ARG_MINSIZE: state->skip_minsize = 1; break; case OPT_ARG_SUPPRESSED: state->skip_suppressed = 1; break; default: error(EXIT_FAILURE, 0, "unknown --skip specifier: %s", value); } } } /*! \brief Command line options parser. */ static void parse_command_line_opts(struct conf_t *state, int argc, char **argv) { enum { OPT_SNET_ALARMS = CHAR_MAX + 1, OPT_WARN, OPT_CRIT, OPT_MINSIZE, OPT_WARN_COUNT, OPT_CRIT_COUNT, OPT_COLOR, OPT_SKIP, OPT_SET_IPV, OPT_MUSTACH }; static struct option const long_options[] = { {"config", required_argument, NULL, 'c'}, {"leases", required_argument, NULL, 'l'}, {"color", required_argument, NULL, OPT_COLOR}, {"skip", required_argument, NULL, OPT_SKIP}, {"format", required_argument, NULL, 'f'}, {"sort", required_argument, NULL, 's'}, {"reverse", no_argument, NULL, 'r'}, {"output", required_argument, NULL, 'o'}, {"limit", required_argument, NULL, 'L'}, {"mustach", required_argument, NULL, OPT_MUSTACH}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {"snet-alarms", no_argument, NULL, OPT_SNET_ALARMS}, {"warning", required_argument, NULL, OPT_WARN}, {"critical", required_argument, NULL, OPT_CRIT}, {"warn-count", required_argument, NULL, OPT_WARN_COUNT}, {"crit-count", required_argument, NULL, OPT_CRIT_COUNT}, {"minsize", required_argument, NULL, OPT_MINSIZE}, {"perfdata", no_argument, NULL, 'p'}, {"all-as-shared", no_argument, NULL, 'A'}, {"ip-version", required_argument, NULL, OPT_SET_IPV}, {NULL, 0, NULL, 0} }; int alarming = 0; while (1) { int c; c = getopt_long(argc, argv, "c:l:f:o:s:rL:pAvh", long_options, NULL); if (c == EOF) break; switch (c) { case 'c': /* config file */ state->dhcpdconf_file = optarg; break; case 'l': /* lease file */ state->dhcpdlease_file = optarg; break; case 'f': /* Output format */ state->output_format = optarg[0]; break; case 's': { /* Output sorting option */ struct output_sort *p = state->sorts; char *ptr = optarg; while (p && p->next) p = p->next; while (*ptr) { if (state->sorts == NULL) { state->sorts = xcalloc(1, sizeof(struct output_sort)); p = state->sorts; } else { p->next = xcalloc(1, sizeof(struct output_sort)); p = p->next; } p->func = field_selector(*ptr++); } } break; case 'r': /* What ever sort in reverse order */ state->reverse_order = 1; break; case 'o': /* Output file */ state->output_file = optarg; break; case 'L': /* Specification what will be printed */ state->header_limit = return_limit(optarg[0]); state->number_limit = return_limit(optarg[1]); break; case OPT_MUSTACH: #ifdef BUILD_MUSTACH state->mustach_template = optarg; state->output_format = 'm'; #else error(EXIT_FAILURE, 0, "compiled without mustach support"); #endif break; case OPT_COLOR: state->color_mode = parse_color_mode(optarg); if (state->color_mode == color_unknown) error(EXIT_FAILURE, errno, "unknown color mode: %s", quote(optarg)); break; case OPT_SKIP: skip_arg_parse(state, optarg); break; case OPT_SNET_ALARMS: state->snet_alarms = 1; break; case OPT_WARN: alarming = 1; state->warning = strtod_or_err(optarg, "illegal argument"); break; case OPT_CRIT: alarming = 1; state->critical = strtod_or_err(optarg, "illegal argument"); break; case OPT_WARN_COUNT: alarming = 1; state->warn_count = strtod_or_err(optarg, "illegal argument"); break; case OPT_CRIT_COUNT: alarming = 1; state->crit_count = strtod_or_err(optarg, "illegal argument"); break; case OPT_MINSIZE: state->minsize = strtod_or_err(optarg, "illegal argument"); break; case OPT_SET_IPV: switch (optarg[0]) { case '4': set_ipv_functions(state, IPv4); break; case '6': set_ipv_functions(state, IPv6); break; default: error(EXIT_FAILURE, 0, "unknown --ip-version argument: %s", optarg); } break; case 'p': /* Print additional performance data in alarming mode */ state->perfdata = 1; break; case 'A': /* Treat single networks as shared with network CIDR as name */ state->all_as_shared = 1; break; case 'v': /* Print version */ print_version(); case 'h': /* Print help */ usage(EXIT_SUCCESS); default: error(EXIT_FAILURE, 0, "Try %s --help for more information.", program_name); } } /* Use default dhcpd.conf when user did not define anything. */ if (state->dhcpdconf_file == NULL) state->dhcpdconf_file = DHCPDCONF_FILE; /* Use default dhcpd.leases when user did not define anything. */ if (state->dhcpdlease_file == NULL) state->dhcpdlease_file = DHCPDLEASE_FILE; /* Use default limits when user did not define anything. */ if (state->header_limit == 8) { char const *default_limit = OUTPUT_LIMIT; state->header_limit = return_limit(default_limit[0]); state->number_limit = return_limit(default_limit[1]); } /* Output format is not defined, if alarm thresholds are then it's alarming, else use the * default. */ if (state->output_format == '\0') { if (alarming == 1) state->output_format = 'a'; else { const char *const default_format = OUTPUT_FORMAT; state->output_format = default_format[0]; } } if (state->output_format == 'X' || state->output_format == 'J') { state->print_mac_addreses = 1; } } /*!\brief Start of execution. This will mostly call other functions one * after another. * * \return Return value indicates success or fail or analysis, unless * either --warning or --critical options are in use, which makes the * return value in some cases to match with Nagios expectations about * alarming. */ int main(int argc, char **argv) { struct conf_t state = { .warning = ALARM_WARN, .critical = ALARM_CRIT, .warn_count = 0x100000000, /* == 2^32 that is the entire IPv4 space */ .crit_count = 0x100000000, /* basically turns off the count criteria */ .header_limit = 8, .ranges_size = 64, .ip_version = IPvUNKNOWN, .color_mode = color_auto, 0 }; int ret_val; atexit(close_stdout); set_program_name(argv[0]); prepare_memory(&state); set_ipv_functions(&state, IPvUNKNOWN); parse_command_line_opts(&state, argc, argv); /* Do the job */ parse_config(&state, 1, state.dhcpdconf_file, state.shared_net_root); parse_leases(&state); prepare_data(&state); do_counting(&state); if (state.sorts != NULL) mergesort_ranges(&state, state.ranges, state.num_ranges, NULL, 1); if (state.reverse_order == 1) flip_ranges(&state); ret_val = output_analysis(&state); clean_up(&state); return (ret_val); }