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 dhcpd-pools.c
37 * \brief The main(), and core initialization.
38 */
39
40 #include <config.h>
41
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <getopt.h>
46 #include <stdio.h>
47 #include <limits.h>
48
49 #include "close-stream.h"
50 #include "closeout.h"
51 #include "error.h"
52 #include "progname.h"
53 #include "quote.h"
54 #include "xalloc.h"
55
56 #include "dhcpd-pools.h"
57
58 /* Function pointers */
59 int (*parse_ipaddr) (struct conf_t *state, const char *restrict src, union ipaddr_t *restrict dst);
60 void (*copy_ipaddr) (union ipaddr_t *restrict dst, const union ipaddr_t *restrict src);
61 const char *(*ntop_ipaddr) (const union ipaddr_t *ip);
62 double (*get_range_size) (const struct range_t *r);
63 int (*xstrstr) (struct conf_t *state, const char *restrict str);
64 int (*ipcomp) (const union ipaddr_t *restrict a, const union ipaddr_t *restrict b);
65 int (*leasecomp) (const struct leases_t *restrict a, const struct leases_t *restrict b);
66 void (*add_lease) (struct conf_t *state, union ipaddr_t *ip, enum ltype type);
67 struct leases_t *(*find_lease) (struct conf_t *state, union ipaddr_t *ip);
68
69 /*! \brief An option argument parser to populate state header_limit and
70 * number_limit values.
71 */
return_limit(const char c)72 static int return_limit(const char c)
73 {
74 if ('0' <= c && c < '8')
75 return c - '0';
76 error(EXIT_FAILURE, 0, "return_limit: output mask %s is illegal", quote(optarg));
77 return 0;
78 }
79
80 /*! \brief Run time initialization. Global allocations, counter
81 * initializations, etc are here. */
prepare_memory(struct conf_t * state)82 static void prepare_memory(struct conf_t *state)
83 {
84 state->ranges = xmalloc(sizeof(struct range_t) * state->ranges_size);
85 /* First shared network entry is all networks */
86 state->shared_net_root = xcalloc(sizeof(struct shared_network_t), 1);
87 state->shared_net_root->name = xstrdup("All networks");
88 state->shared_net_head = state->shared_net_root;
89 }
90
91 /*! \brief The --skip option argument parser. */
skip_arg_parse(struct conf_t * state,char * arg)92 static void skip_arg_parse(struct conf_t *state, char *arg)
93 {
94 enum {
95 OPT_ARG_OK = 0,
96 OPT_ARG_WARNING,
97 OPT_ARG_CRITICAL,
98 OPT_ARG_MINSIZE,
99 OPT_ARG_SUPPRESSED
100 };
101
102 char *const tokens[] = {
103 [OPT_ARG_OK] = "ok",
104 [OPT_ARG_WARNING] = "warning",
105 [OPT_ARG_CRITICAL] = "critical",
106 [OPT_ARG_MINSIZE] = "minsize",
107 [OPT_ARG_SUPPRESSED] = "suppressed",
108 NULL
109 };
110 char *value;
111
112 while (*arg != '\0') {
113 switch (getsubopt(&arg, tokens, &value)) {
114 case OPT_ARG_OK:
115 state->skip_ok = 1;
116 break;
117 case OPT_ARG_WARNING:
118 state->skip_warning = 1;
119 break;
120 case OPT_ARG_CRITICAL:
121 state->skip_critical = 1;
122 break;
123 case OPT_ARG_MINSIZE:
124 state->skip_minsize = 1;
125 break;
126 case OPT_ARG_SUPPRESSED:
127 state->skip_suppressed = 1;
128 break;
129 default:
130 error(EXIT_FAILURE, 0, "unknown --skip specifier: %s", value);
131 }
132 }
133 }
134
135 /*! \brief Command line options parser. */
parse_command_line_opts(struct conf_t * state,int argc,char ** argv)136 static void parse_command_line_opts(struct conf_t *state, int argc, char **argv)
137 {
138 enum {
139 OPT_SNET_ALARMS = CHAR_MAX + 1,
140 OPT_WARN,
141 OPT_CRIT,
142 OPT_MINSIZE,
143 OPT_WARN_COUNT,
144 OPT_CRIT_COUNT,
145 OPT_COLOR,
146 OPT_SKIP,
147 OPT_SET_IPV,
148 OPT_MUSTACH
149 };
150
151 static struct option const long_options[] = {
152 {"config", required_argument, NULL, 'c'},
153 {"leases", required_argument, NULL, 'l'},
154 {"color", required_argument, NULL, OPT_COLOR},
155 {"skip", required_argument, NULL, OPT_SKIP},
156 {"format", required_argument, NULL, 'f'},
157 {"sort", required_argument, NULL, 's'},
158 {"reverse", no_argument, NULL, 'r'},
159 {"output", required_argument, NULL, 'o'},
160 {"limit", required_argument, NULL, 'L'},
161 {"mustach", required_argument, NULL, OPT_MUSTACH},
162 {"version", no_argument, NULL, 'v'},
163 {"help", no_argument, NULL, 'h'},
164 {"snet-alarms", no_argument, NULL, OPT_SNET_ALARMS},
165 {"warning", required_argument, NULL, OPT_WARN},
166 {"critical", required_argument, NULL, OPT_CRIT},
167 {"warn-count", required_argument, NULL, OPT_WARN_COUNT},
168 {"crit-count", required_argument, NULL, OPT_CRIT_COUNT},
169 {"minsize", required_argument, NULL, OPT_MINSIZE},
170 {"perfdata", no_argument, NULL, 'p'},
171 {"all-as-shared", no_argument, NULL, 'A'},
172 {"ip-version", required_argument, NULL, OPT_SET_IPV},
173 {NULL, 0, NULL, 0}
174 };
175 int alarming = 0;
176
177 while (1) {
178 int c;
179
180 c = getopt_long(argc, argv, "c:l:f:o:s:rL:pAvh", long_options, NULL);
181 if (c == EOF)
182 break;
183 switch (c) {
184 case 'c':
185 /* config file */
186 state->dhcpdconf_file = optarg;
187 break;
188 case 'l':
189 /* lease file */
190 state->dhcpdlease_file = optarg;
191 break;
192 case 'f':
193 /* Output format */
194 state->output_format = optarg[0];
195 break;
196 case 's':
197 {
198 /* Output sorting option */
199 struct output_sort *p = state->sorts;
200 char *ptr = optarg;
201
202 while (p && p->next)
203 p = p->next;
204 while (*ptr) {
205 if (state->sorts == NULL) {
206 state->sorts =
207 xcalloc(1, sizeof(struct output_sort));
208 p = state->sorts;
209 } else {
210 p->next = xcalloc(1, sizeof(struct output_sort));
211 p = p->next;
212 }
213 p->func = field_selector(*ptr++);
214 }
215 }
216 break;
217 case 'r':
218 /* What ever sort in reverse order */
219 state->reverse_order = 1;
220 break;
221 case 'o':
222 /* Output file */
223 state->output_file = optarg;
224 break;
225 case 'L':
226 /* Specification what will be printed */
227 state->header_limit = return_limit(optarg[0]);
228 state->number_limit = return_limit(optarg[1]);
229 break;
230 case OPT_MUSTACH:
231 #ifdef BUILD_MUSTACH
232 state->mustach_template = optarg;
233 state->output_format = 'm';
234 state->print_mac_addreses = 1;
235 #else
236 error(EXIT_FAILURE, 0, "compiled without mustach support");
237 #endif
238 break;
239 case OPT_COLOR:
240 state->color_mode = parse_color_mode(optarg);
241 if (state->color_mode == color_unknown)
242 error(EXIT_FAILURE, errno, "unknown color mode: %s", quote(optarg));
243 break;
244 case OPT_SKIP:
245 skip_arg_parse(state, optarg);
246 break;
247 case OPT_SNET_ALARMS:
248 state->snet_alarms = 1;
249 break;
250 case OPT_WARN:
251 alarming = 1;
252 state->warning = strtod_or_err(optarg, "illegal argument");
253 break;
254 case OPT_CRIT:
255 alarming = 1;
256 state->critical = strtod_or_err(optarg, "illegal argument");
257 break;
258 case OPT_WARN_COUNT:
259 alarming = 1;
260 state->warn_count = strtod_or_err(optarg, "illegal argument");
261 break;
262 case OPT_CRIT_COUNT:
263 alarming = 1;
264 state->crit_count = strtod_or_err(optarg, "illegal argument");
265 break;
266 case OPT_MINSIZE:
267 state->minsize = strtod_or_err(optarg, "illegal argument");
268 break;
269 case OPT_SET_IPV:
270 switch (optarg[0]) {
271 case '4':
272 set_ipv_functions(state, IPv4);
273 break;
274 case '6':
275 set_ipv_functions(state, IPv6);
276 break;
277 default:
278 error(EXIT_FAILURE, 0, "unknown --ip-version argument: %s", optarg);
279 }
280 break;
281 case 'p':
282 /* Print additional performance data in alarming mode */
283 state->perfdata = 1;
284 break;
285 case 'A':
286 /* Treat single networks as shared with network CIDR as name */
287 state->all_as_shared = 1;
288 break;
289 case 'v':
290 /* Print version */
291 print_version();
292 case 'h':
293 /* Print help */
294 usage(EXIT_SUCCESS);
295 default:
296 error(EXIT_FAILURE, 0, "Try %s --help for more information.", program_name);
297 }
298 }
299
300 /* Use default dhcpd.conf when user did not define anything. */
301 if (state->dhcpdconf_file == NULL)
302 state->dhcpdconf_file = DHCPDCONF_FILE;
303 /* Use default dhcpd.leases when user did not define anything. */
304 if (state->dhcpdlease_file == NULL)
305 state->dhcpdlease_file = DHCPDLEASE_FILE;
306 /* Use default limits when user did not define anything. */
307 if (state->header_limit == 8) {
308 char const *default_limit = OUTPUT_LIMIT;
309
310 state->header_limit = return_limit(default_limit[0]);
311 state->number_limit = return_limit(default_limit[1]);
312 }
313 /* Output format is not defined, if alarm thresholds are then it's alarming, else use the
314 * default. */
315 if (state->output_format == '\0') {
316 if (alarming == 1)
317 state->output_format = 'a';
318 else {
319 const char *const default_format = OUTPUT_FORMAT;
320
321 state->output_format = default_format[0];
322 }
323 }
324 if (state->output_format == 'X' || state->output_format == 'J') {
325 state->print_mac_addreses = 1;
326 }
327 }
328
329 /*!\brief Start of execution. This will mostly call other functions one
330 * after another.
331 *
332 * \return Return value indicates success or fail or analysis, unless
333 * either --warning or --critical options are in use, which makes the
334 * return value in some cases to match with Nagios expectations about
335 * alarming. */
main(int argc,char ** argv)336 int main(int argc, char **argv)
337 {
338 struct conf_t state = {
339 .warning = ALARM_WARN,
340 .critical = ALARM_CRIT,
341 .warn_count = 0x100000000, /* == 2^32 that is the entire IPv4 space */
342 .crit_count = 0x100000000, /* basically turns off the count criteria */
343 .header_limit = 8,
344 .ranges_size = 64,
345 .ip_version = IPvUNKNOWN,
346 .color_mode = color_auto,
347 0
348 };
349 int ret_val;
350
351 atexit(close_stdout);
352 set_program_name(argv[0]);
353 prepare_memory(&state);
354 set_ipv_functions(&state, IPvUNKNOWN);
355 parse_command_line_opts(&state, argc, argv);
356
357 /* Do the job */
358 parse_config(&state, 1, state.dhcpdconf_file, state.shared_net_root);
359 parse_leases(&state);
360 prepare_data(&state);
361 do_counting(&state);
362 if (state.sorts != NULL)
363 mergesort_ranges(&state, state.ranges, state.num_ranges, NULL, 1);
364 if (state.reverse_order == 1)
365 flip_ranges(&state);
366 ret_val = output_analysis(&state);
367 clean_up(&state);
368 return (ret_val);
369 }
370