1 /*-
2  * Copyright (c) 2005 Andrey Simonenko
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 
29 #ifndef lint
30 static const char rcsid[] ATTR_UNUSED =
31   "@(#)$Id: ipastat_main.c,v 1.2.2.1 2012/07/09 20:28:19 simon Exp $";
32 #endif /* !lint */
33 
34 #include <sys/types.h>
35 
36 #include <regex.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "ipa_mod.h"
43 
44 #include "queue.h"
45 
46 #include "dlapi.h"
47 #include "confcommon.h"
48 #include "memfunc.h"
49 
50 #include "ipastat_conf.h"
51 #include "ipastat_log.h"
52 #include "ipastat_main.h"
53 #include "ipastat_rules.h"
54 #include "ipastat_time.h"
55 #include "ipastat_st.h"
56 
57 const char	*x_pat = NULL;		/* -q -x pattern */
58 regex_t		x_reg;			/* Compiled x_pat. */
59 regex_t		*x_reg_ptr = NULL;	/* NULL or &x_reg. */
60 
61 unsigned int	a_flag = A_FLAG_ABSENT;	/* -q -a ... */
62 
63 ipa_mem_type	*m_anon;		/* Anonymous memory allocations. */
64 ipa_mem_type	*m_result;		/* Memory used for query results. */
65 
66 static char	need_nl_raw = 0;	/* Set if new line is needed
67 					   in raw output. */
68 
69 /*
70  * If some rule has several buffers with statistics for
71  * several time intervals, then keep them in rule_stat_list
72  * structure.
73  */
74 struct rule_stat {
75 	STAILQ_ENTRY(rule_stat) link;	/* All rule_stat for rule. */
76 	unsigned int	n;		/* Number of elements in buf. */
77 	struct ipa_rule_stat *buf;	/* Buffer with statistics for rule. */
78 	const struct opt_tint *tint;	/* Time interval. */
79 };
80 
81 STAILQ_HEAD(rule_stat_list, rule_stat);
82 
83 #ifdef WITH_LIMITS
84 /*
85  * If some limit has several buffers with statistics for
86  * several time intervals, then keep them in limit_stat_list
87  * structure.
88  */
89 struct limit_stat {
90 	STAILQ_ENTRY(limit_stat) link;	/* All limit_stat for limit. */
91 	unsigned int	n;		/* Number of elements in buf. */
92 	struct ipa_limit_state *buf;	/* Buffer with statistics for limit. */
93 	const struct opt_tint *tint;	/* Time interval. */
94 };
95 
96 STAILQ_HEAD(limit_stat_list, limit_stat);
97 #endif /* WITH_LIMITS */
98 
99 #ifdef WITH_ANY_LIMITS
100 /*
101  * Description of several entities.
102  */
103 struct entity_desc {
104 	unsigned int	n;		/* Number of elements in desc_list. */
105 	struct ipa_entity_desc *desc_list; /* Array of descriptions. */
106 };
107 #endif
108 
109 static int	output(const char *, ...) ATTR_FORMAT(printf, 1, 2);
110 
111 static int
output(const char * format,...)112 output(const char *format, ...)
113 {
114 	va_list ap;
115 
116 	va_start(ap, format);
117 	if (vprintf(format, ap) < 0) {
118 		logmsg(IPA_LOG_ERR, "output: vprintf failed");
119 		return (-1);
120 	}
121 	va_end(ap);
122 	return (0);
123 }
124 
125 static int
print_line(int len)126 print_line(int len)
127 {
128 	while (len--)
129 		if (printf("-") < 0) {
130 			logmsg(IPA_LOG_ERR, "print_line: printf failed");
131 			return (-1);
132 		}
133 	return (0);
134 }
135 
136 static int
output_flush(void)137 output_flush(void)
138 {
139 	if (fflush(stdout) != 0) {
140 		logmsg(IPA_LOG_ERR, "output_flush: fflush(stdout)");
141 		return (-1);
142 	}
143 	return (0);
144 }
145 
146 static int
output_ipa_tm(const char * msg,char sep,const ipa_tm * tm,int is_set)147 output_ipa_tm(const char *msg, char sep, const ipa_tm *tm, int is_set)
148 {
149 	if (output("%s %c ", msg, sep) < 0)
150 		goto failed;
151 	if (is_set) {
152 		if (output("%d.%02d.%02d/%02d:%02d:%02d\n",
153 		    tm->tm_year, tm->tm_mon, tm->tm_mday,
154 		    tm->tm_hour, tm->tm_min, tm->tm_sec) < 0)
155 			goto failed;
156 	} else {
157 		if (output("-\n") < 0)
158 			goto failed;
159 	}
160 	return (0);
161 
162 failed:
163 	logbt("output_ipa_tm");
164 	return (-1);
165 }
166 
167 static int
cmp_entity_desc(const void * p1,const void * p2)168 cmp_entity_desc(const void *p1, const void *p2)
169 {
170 	return (strcmp(
171 	    ((const struct ipa_entity_desc *)p1)->name,
172 	    ((const struct ipa_entity_desc *)p2)->name));
173 }
174 
175 static int
output_desc_list_raw(const char * what,unsigned int n,const struct ipa_entity_desc * desc_list)176 output_desc_list_raw(const char *what, unsigned int n,
177     const struct ipa_entity_desc *desc_list)
178 {
179 	size_t len, name_width, info_width;
180 	const struct ipa_entity_desc *desc;
181 	int nwidth, iwidth;
182 
183 	name_width = strlen(what);
184 	info_width = sizeof("info") - 1;
185 	for (desc = desc_list; desc < desc_list + n; ++desc) {
186 		len = strlen(desc->name);
187 		if (name_width < len)
188 			name_width = len;
189 		if (desc->info != NULL) {
190 			len = strlen(desc->info);
191 			if (info_width < len)
192 				info_width = len;
193 		}
194 	}
195 	++info_width;
196 
197 	nwidth = (int)name_width;
198 	iwidth = (int)info_width;
199 
200 	if (output("%-*s | Info\n", nwidth, what) < 0)
201 		goto failed;
202 
203 	if (print_line(nwidth) < 0 || output("-+") < 0 ||
204 	    print_line(iwidth) < 0 || output("\n") < 0)
205 		goto failed;
206 
207 	for (desc = desc_list; desc < desc_list + n; ++desc)
208 		if ((desc->info != NULL ?
209 		    output("%-*s | %s\n", nwidth, desc->name, desc->info) :
210 		    output("%-*s |\n", nwidth, desc->name)) < 0)
211 			goto failed;
212 
213 	if (print_line(nwidth) < 0 || output("-+") < 0 ||
214 	    print_line(iwidth) < 0 || output("\n\n") < 0)
215 		goto failed;
216 
217 	if (output(" * %u line%s\n", n, plural_form(n)) < 0)
218 		goto failed;
219 
220 	if (output_flush() < 0)
221 		goto failed;
222 
223 	return (0);
224 
225 failed:
226 	logbt("output_entity_desc_raw");
227 	return (-1);
228 }
229 
230 static void
free_desc_list(unsigned int n,struct ipa_entity_desc * desc_list)231 free_desc_list(unsigned int n, struct ipa_entity_desc *desc_list)
232 {
233 	if (desc_list != NULL) {
234 		struct ipa_entity_desc *desc;
235 
236 		for (desc = desc_list; desc < desc_list + n; ++desc) {
237 			mem_free(desc->name, m_result);
238 			mem_free(desc->info, m_result);
239 		}
240 		mem_free(desc_list, m_result);
241 	}
242 }
243 
244 static int
show_rules_list(void)245 show_rules_list(void)
246 {
247 	const struct st_list *st_list;
248 	struct ipa_entity_desc *desc_list;
249 	unsigned int n;
250 	int rv;
251 
252 	st_list = cur_opt_st != NULL ? cur_opt_st->st_list : global_st_list;
253 	rv = -1;
254 
255 	if (st_get_rules_list(st_list, &n, &desc_list) < 0) {
256 		desc_list = NULL;
257 		goto done;
258 	}
259 	qsort(desc_list, n, sizeof(*desc_list), cmp_entity_desc);
260 	if (output_desc_list_raw("Rule", n, desc_list) < 0)
261 		goto done;
262 	rv = 0;
263 done:
264 	free_desc_list(n, desc_list);
265 	if (rv != 0)
266 		logbt("show_rules_list");
267 	return (rv);
268 }
269 
270 static int
output_rule_stat_raw(const struct opt_rule * opt_rule)271 output_rule_stat_raw(const struct opt_rule *opt_rule)
272 {
273 	uint64_t cnt_day, cnt_sum, cnt_tot;
274 	const struct rule *rule;
275 	const struct ipa_rule_stat *stat, *stat_end;
276 	const struct rule_stat *rule_stat;
277 	const struct rule_stat_list *rule_stat_list;
278 	unsigned int curyear, curmon, curmday;
279 	unsigned int ndays;
280 
281 	if (need_nl_raw) {
282 		if (output("\n") < 0)
283 			goto failed;
284 	} else
285 		need_nl_raw = 1;
286 
287 	rule = opt_rule->rule;
288 	if (output("Rule : %s\nInfo : %s\n", rule->name,
289 	    rule->info != NULL ? rule->info : "") < 0)
290 		goto failed;
291 
292 	rule_stat_list = opt_rule->data;
293 	cnt_tot = 0;
294 
295 	STAILQ_FOREACH(rule_stat, rule_stat_list, link) {
296 		if (output_ipa_tm("\nFrom", ':', &rule_stat->tint->tm1, 1) < 0)
297 			goto failed;
298 		if (output_ipa_tm("To  ", ':', &rule_stat->tint->tm2, 1) < 0)
299 			goto failed;
300 		cnt_sum = 0;
301 		if (rule_stat->n != 0) {
302 			stat = rule_stat->buf;
303 			stat_end = stat + rule_stat->n;
304 			curyear = stat->year;
305 			curmon = stat->mon;
306 			curmday = stat->mday;
307 			cnt_day = 0;
308 			ndays = 1;
309 			if (output("\nTimestamp                    |"
310 			    "	            Counter |              Per day\n"
311 			    "-----------------------------+-----------"
312 			    "-----------+---------------------") < 0)
313 				goto failed;
314 			for (;;) {
315 				if (output("\n%u.%02u.%02u/%02u:%02u:%02u-"
316 				    "%02u:%02u:%02u | %20"PRIu64" |",
317 				    stat->year, stat->mon, stat->mday,
318 				    stat->h1, stat->m1, stat->s1,
319 				    stat->h2, stat->m2, stat->s2,
320 				    stat->cnt) < 0)
321 					goto failed;
322 				if (cnt_day < UINT64_MAX - stat->cnt)
323 					cnt_day += stat->cnt;
324 				else
325 					cnt_day = UINT64_MAX;
326 				if (++stat == stat_end)
327 					break;
328 				if (curmday != stat->mday ||
329 				    curmon != stat->mon ||
330 				    curyear != stat->year) {
331 					curmday = stat->mday;
332 					curmon = stat->mon;
333 					curyear = stat->year;
334 					ndays++;
335 					if (cnt_sum < UINT64_MAX - cnt_day)
336 						cnt_sum += cnt_day;
337 					else
338 						cnt_sum = UINT64_MAX;
339 					if (cnt_day != UINT64_MAX) {
340 						if (output(" %20"PRIu64,
341 						    cnt_day) < 0)
342 							goto failed;
343 					} else {
344 						if (output(" %20s",
345 						    "TOO_BIG") < 0)
346 							goto failed;
347 					}
348 					cnt_day = 0;
349 				}
350 			}
351 			if (cnt_day != UINT64_MAX) {
352 				if (output(" %20"PRIu64, cnt_day) < 0)
353 					goto failed;
354 			} else {
355 				if (output(" %20s", "TOO_BIG") < 0)
356 					goto failed;
357 			}
358 			if (output("\n-----------------------------+"
359 			    "----------------------+----------------"
360 			    "-----") < 0)
361 				goto failed;
362 
363 			if (cnt_sum < UINT64_MAX - cnt_day)
364 				cnt_sum += cnt_day;
365 			else
366 				cnt_sum = UINT64_MAX;
367 		} else
368 			ndays = 0;
369 		if (cnt_sum != UINT64_MAX) {
370 			if (output("\n * Summary %"PRIu64" (%u day%s)\n",
371 			    cnt_sum, ndays, plural_form(ndays)) < 0)
372 				goto failed;
373 		} else {
374 			if (output("\n * Summary TOO_BIG (%u day%s)\n",
375 			    ndays, plural_form(ndays)) < 0)
376 				goto failed;
377 		}
378 		if (cnt_tot < UINT64_MAX - cnt_sum)
379 			cnt_tot += cnt_sum;
380 		else
381 			cnt_tot = UINT64_MAX;
382 	}
383 
384 	if (cnt_tot != UINT64_MAX) {
385 		if (output("\n * Total %"PRIu64"\n", cnt_tot) < 0)
386 			goto failed;
387 	} else {
388 		if (output("\n * Total TOO_BIG\n") < 0)
389 			goto failed;
390 	}
391 
392 	if (output_flush() < 0)
393 		goto failed;
394 
395 	return (0);
396 
397 failed:
398 	logbt("output_rule_stat_raw");
399 	return (-1);
400 }
401 
402 static int
show_rules_stat(void)403 show_rules_stat(void)
404 {
405 	const struct opt_tint *tint;
406 	struct ipa_rule_stat *buf;
407 	struct rule *rule;
408 	struct opt_rule *opt_rule;
409 	struct rule_stat *rule_stat, *rule_stat_next;
410 	struct rule_stat_list *rule_stat_list;
411 	unsigned int n;
412 	int rv;
413 
414 	rv = -1;
415 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
416 		rule = opt_rule->rule;
417 		if (rule->info == NULL)
418 			if (st_get_rule_info(rule, &rule->info) < 0)
419 				goto done;
420 		rule_stat_list = mem_malloc(sizeof(*rule_stat_list), m_anon);
421 		if (rule_stat_list == NULL)
422 			goto done;
423 		STAILQ_INIT(rule_stat_list);
424 		opt_rule->data = rule_stat_list;
425 
426 		STAILQ_FOREACH(tint, &opt_tint_list, link) {
427 			if (st_get_rule_stat(rule, &tint->tm1,
428 			    &tint->tm2, tint->exact, &n, &buf) < 0)
429 				goto done;
430 			rule_stat = mem_malloc(sizeof(*rule_stat), m_anon);
431 			if (rule_stat == NULL) {
432 				mem_free(buf, m_result);
433 				goto done;
434 			}
435 			rule_stat->n = n;
436 			rule_stat->buf = buf;
437 			rule_stat->tint = tint;
438 			STAILQ_INSERT_TAIL(rule_stat_list, rule_stat, link);
439 		}
440 	}
441 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link)
442 		if (output_rule_stat_raw(opt_rule) < 0)
443 			goto done;
444 	rv = 0;
445 done:
446 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
447 		rule_stat_list = opt_rule->data;
448 		if (rule_stat_list != NULL) {
449 			STAILQ_FOREACH_SAFE(rule_stat, rule_stat_list, link,
450 			    rule_stat_next) {
451 				mem_free(rule_stat->buf, m_result);
452 				mem_free(rule_stat, m_anon);
453 			}
454 			mem_free(rule_stat_list, m_anon);
455 		}
456 	}
457 	if (rv != 0)
458 		logbt("show_rules_stat");
459 	return (rv);
460 }
461 
462 #ifdef WITH_ANY_LIMITS
463 static int
output_any_limits_list_raw(const struct opt_rule * opt_rule,const char * what)464 output_any_limits_list_raw(const struct opt_rule *opt_rule, const char *what)
465 {
466 	const struct rule *rule;
467 	const struct entity_desc *entity_desc;
468 
469 	if (need_nl_raw) {
470 		if (output("\n") < 0)
471 			goto failed;
472 	} else
473 		need_nl_raw = 1;
474 
475 	rule = opt_rule->rule;
476 	if (output("Rule : %s\nInfo : %s\n\n", rule->name,
477 	    rule->info != NULL ? rule->info : "") < 0)
478 		goto failed;
479 
480 	entity_desc = opt_rule->data;
481 
482 	if (output_desc_list_raw(what, entity_desc->n,
483 	    entity_desc->desc_list) < 0)
484 		goto failed;
485 
486 	if (output_flush() < 0)
487 		goto failed;
488 
489 	return (0);
490 
491 failed:
492 	logbt("output_any_limits_list_raw");
493 	return (-1);
494 }
495 #endif /* WITH_ANY_LIMITS */
496 
497 #ifdef WITH_LIMITS
498 static int
show_limits_list(void)499 show_limits_list(void)
500 {
501 	struct rule *rule;
502 	struct opt_rule *opt_rule;
503 	struct entity_desc *entity_desc;
504 	struct ipa_entity_desc *desc_list;
505 	unsigned int n;
506 	int rv;
507 
508 	rv = -1;
509 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
510 		rule = opt_rule->rule;
511 		if (rule->info == NULL)
512 			if (st_get_rule_info(rule, &rule->info) < 0)
513 				goto done;
514 		if (st_get_limits_list(rule, &n, &desc_list) < 0)
515 			goto done;
516 		entity_desc = mem_malloc(sizeof(*entity_desc), m_anon);
517 		if (entity_desc == NULL) {
518 			free_desc_list(n, desc_list);
519 			goto done;
520 		}
521 		entity_desc->desc_list = desc_list;
522 		entity_desc->n = n;
523 		opt_rule->data = entity_desc;
524 		qsort(desc_list, n, sizeof(*desc_list), cmp_entity_desc);
525 	}
526 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link)
527 		if (output_any_limits_list_raw(opt_rule, "Limit") < 0)
528 			goto done;
529 
530 	rv = 0;
531 done:
532 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
533 		entity_desc = opt_rule->data;
534 		if (entity_desc != NULL) {
535 			free_desc_list(entity_desc->n, entity_desc->desc_list);
536 			mem_free(entity_desc, m_anon);
537 		}
538 	}
539 	if (rv != 0)
540 		logbt("show_limits_list");
541 	return (rv);
542 }
543 
544 #define LIMIT_START_STR		"Start       "
545 #define LIMIT_RESTART_STR	"Restart     "
546 #define LIMIT_RESTART_EXEC_STR	"Restart exec"
547 #define LIMIT_REACH_STR		"Reach       "
548 #define LIMIT_REACH_EXEC_STR	"Reach exec  "
549 #define LIMIT_EXPIRE_STR	"Expire      "
550 #define LIMIT_EXPIRE_EXEC_STR	"Expire exec "
551 #define LIMIT_UPDATED_STR	"Updated     "
552 
553 struct limit_event {
554 	const char	*name;
555 	unsigned int	set;
556 	unsigned int	idx;
557 };
558 
559 #define EVENT_TBL_ENTRY(X) {					\
560 	LIMIT_ ## X ## _STR, IPA_LIMIT_EVENT_ ## X ## _SET,	\
561 	IPA_LIMIT_EVENT_ ## X					\
562 }
563 
564 static const struct limit_event limit_event_tbl[] = {
565 	EVENT_TBL_ENTRY(START),
566 	EVENT_TBL_ENTRY(RESTART),
567 	EVENT_TBL_ENTRY(RESTART_EXEC),
568 	EVENT_TBL_ENTRY(REACH),
569 	EVENT_TBL_ENTRY(REACH_EXEC),
570 	EVENT_TBL_ENTRY(EXPIRE),
571 	EVENT_TBL_ENTRY(EXPIRE_EXEC),
572 	EVENT_TBL_ENTRY(UPDATED)
573 };
574 
575 #undef EVENT_TBL_ENTRY
576 
577 static int
output_limit_stat_raw(const struct opt_limit * opt_limit)578 output_limit_stat_raw(const struct opt_limit *opt_limit)
579 {
580 	const struct limit *limit;
581 	const struct ipa_limit_state *state, *state_end;
582 	const struct limit_stat *limit_stat;
583 	const struct limit_stat_list *limit_stat_list;
584 	const struct limit_event *event;
585 
586 	limit = opt_limit->limit;
587 	if (output("\nLimit : %s\nInfo  : %s\n",
588 	    limit->name, limit->info != NULL ? limit->info : "") < 0)
589 		goto failed;
590 
591 	limit_stat_list = opt_limit->data;
592 
593 	STAILQ_FOREACH(limit_stat, limit_stat_list, link) {
594 		if (limit_stat->tint == NULL) {
595 			if (output("\nFrom  : current\nTo    : current\n") < 0)
596 				goto failed;
597 		} else {
598 			if (output_ipa_tm("\nFrom ", ':',
599 			    &limit_stat->tint->tm1, 1) < 0)
600 				goto failed;
601 			if (output_ipa_tm("To   ", ':',
602 			    &limit_stat->tint->tm2, 1) < 0)
603 				goto failed;
604 		}
605 		state = limit_stat->buf;
606 		state_end = state + limit_stat->n;
607 		for (; state != state_end; ++state) {
608 			if (output("\n-------------+"
609 			    "---------------------\n") < 0)
610 				goto failed;
611 			for (event = limit_event_tbl;
612 			    event < limit_event_tbl +
613 			    IPA_LIMIT_EVENT_NUM; ++event)
614 				if (output_ipa_tm(event->name, '|',
615 				    &state->event_date[event->idx],
616 				    state->event_date_set & event->set) < 0)
617 					goto failed;
618 			if (output("Limit        | %20"PRIu64
619 			    "\nCounter      | %20"PRIu64"",
620 			    state->lim, state->cnt) < 0)
621 				goto failed;
622 		}
623 		if (limit_stat->n != 0)
624 			if (output("\n-------------+"
625 			    "---------------------\n") < 0)
626 				goto failed;
627 		if (output("\n * %u state%s\n", limit_stat->n,
628 		    plural_form(limit_stat->n)) < 0)
629 			goto failed;
630 	}
631 
632 	return (0);
633 
634 failed:
635 	logbt("output_limit_stat_raw");
636 	return (-1);
637 }
638 
639 static int
output_limits_stat_raw(const struct opt_rule * opt_rule)640 output_limits_stat_raw(const struct opt_rule *opt_rule)
641 {
642 	const struct rule *rule;
643 	const struct opt_limit *opt_limit;
644 
645 	if (need_nl_raw) {
646 		if (output("\n") < 0)
647 			goto failed;
648 	} else
649 		need_nl_raw = 1;
650 
651 	rule = opt_rule->rule;
652 	if (output("Rule  : %s\nInfo  : %s\n",
653 	    rule->name, rule->info != NULL ? rule->info : "") < 0)
654 		goto failed;
655 
656 	STAILQ_FOREACH(opt_limit, &opt_rule->opt_limits, link)
657 		if (output_limit_stat_raw(opt_limit) < 0)
658 			goto failed;
659 
660 	if (output_flush() < 0)
661 		goto failed;
662 
663 	return (0);
664 
665 failed:
666 	logbt("output_limits_stat_raw");
667 	return (-1);
668 }
669 
670 static int
show_limits_stat(void)671 show_limits_stat(void)
672 {
673 	const struct opt_tint *tint;
674 	struct ipa_limit_state *buf;
675 	struct rule *rule;
676 	struct limit *limit;
677 	struct opt_rule *opt_rule;
678 	struct opt_limit *opt_limit;
679 	struct limit_stat *limit_stat, *limit_stat_next;
680 	struct limit_stat_list *limit_stat_list;
681 	unsigned int n;
682 	int rv;
683 
684 	rv = -1;
685 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
686 		rule = opt_rule->rule;
687 		if (rule->info == NULL)
688 			if (st_get_rule_info(rule, &rule->info) < 0)
689 				goto done;
690 		STAILQ_FOREACH(opt_limit, &opt_rule->opt_limits, link) {
691 			limit = opt_limit->limit;
692 			if (limit->info == NULL)
693 				if (st_get_limit_info(rule, limit,
694 				    &limit->info) < 0)
695 					goto done;
696 			limit_stat_list = mem_malloc(sizeof(*limit_stat_list),
697 			    m_anon);
698 			if (limit_stat_list == NULL)
699 				goto done;
700 			STAILQ_INIT(limit_stat_list);
701 			opt_limit->data = limit_stat_list;
702 
703 			if (STAILQ_EMPTY(&opt_tint_list)) {
704 				if (st_get_limit_stat(rule, limit,
705 				    (ipa_tm *)NULL, (ipa_tm *)NULL, &n,
706 				    &buf) < 0)
707 					goto done;
708 				limit_stat = mem_malloc(sizeof(*limit_stat),
709 				    m_anon);
710 				if (limit_stat == NULL) {
711 					mem_free(buf, m_result);
712 					goto done;
713 				}
714 				limit_stat->n = n;
715 				limit_stat->buf = buf;
716 				limit_stat->tint = NULL;
717 				STAILQ_INSERT_TAIL(limit_stat_list, limit_stat,
718 				    link);
719 			} else STAILQ_FOREACH(tint, &opt_tint_list, link) {
720 				if (st_get_limit_stat(rule, limit, &tint->tm1,
721 				    &tint->tm2, &n, &buf) < 0)
722 					goto done;
723 				limit_stat = mem_malloc(sizeof(*limit_stat),
724 				    m_anon);
725 				if (limit_stat == NULL) {
726 					mem_free(buf, m_result);
727 					goto done;
728 				}
729 				limit_stat->n = n;
730 				limit_stat->buf = buf;
731 				limit_stat->tint = tint;
732 				STAILQ_INSERT_TAIL(limit_stat_list, limit_stat,
733 				    link);
734 			}
735 		}
736 	}
737 
738 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link)
739 		if (output_limits_stat_raw(opt_rule) < 0)
740 			goto done;
741 
742 	rv = 0;
743 done:
744 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link)
745 		STAILQ_FOREACH(opt_limit, &opt_rule->opt_limits, link) {
746 			limit_stat_list = opt_limit->data;
747 			if (limit_stat_list != NULL) {
748 				STAILQ_FOREACH_SAFE(limit_stat, limit_stat_list,
749 				    link, limit_stat_next) {
750 					mem_free(limit_stat->buf, m_result);
751 					mem_free(limit_stat, m_anon);
752 				}
753 				mem_free(limit_stat_list, m_anon);
754 			}
755 		}
756 
757 	if (rv != 0)
758 		logbt("show_limits_stat");
759 	return (rv);
760 }
761 
762 
763 #endif /* WITH_LIMITS */
764 
765 #ifdef WITH_THRESHOLDS
766 static int
show_thresholds_list(void)767 show_thresholds_list(void)
768 {
769 	struct rule *rule;
770 	struct entity_desc *entity_desc;
771 	struct ipa_entity_desc *desc_list;
772 	struct opt_rule *opt_rule;
773 	unsigned int n;
774 	int rv;
775 
776 	rv = -1;
777 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
778 		rule = opt_rule->rule;
779 		if (rule->info == NULL)
780 			if (st_get_rule_info(rule, &rule->info) < 0)
781 				goto done;
782 		if (st_get_thresholds_list(rule, &n, &desc_list) < 0)
783 			goto done;
784 		entity_desc = mem_malloc(sizeof(*entity_desc), m_anon);
785 		if (entity_desc == NULL) {
786 			free_desc_list(n, desc_list);
787 			goto done;
788 		}
789 		entity_desc->desc_list = desc_list;
790 		entity_desc->n = n;
791 		opt_rule->data = entity_desc;
792 		qsort(desc_list, n, sizeof(*desc_list), cmp_entity_desc);
793 	}
794 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link)
795 		if (output_any_limits_list_raw(opt_rule, "Threshold") < 0)
796 			goto done;
797 	rv = 0;
798 done:
799 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
800 		entity_desc = opt_rule->data;
801 		if (entity_desc != NULL) {
802 			free_desc_list(entity_desc->n, entity_desc->desc_list);
803 			mem_free(entity_desc, m_anon);
804 		}
805 	}
806 	if (rv != 0)
807 		logbt("show_thresholds_list");
808 	return (rv);
809 }
810 
811 static int
output_threshold_stat_raw(const struct opt_threshold * opt_threshold)812 output_threshold_stat_raw(const struct opt_threshold *opt_threshold)
813 {
814 	const struct threshold *threshold;
815 	const struct ipa_threshold_state *state;
816 
817 	threshold = opt_threshold->threshold;
818 	if (output("\nThreshold : %s\nInfo      : %s\n\n",
819 	    threshold->name,
820 	    threshold->info != NULL ? threshold->info : "") < 0)
821 		goto failed;
822 
823 	state = &opt_threshold->state;
824 
825 	if (opt_threshold->reged) {
826 		if (output("----------+---------------------\n") < 0)
827 			goto failed;
828 		if (output_ipa_tm("Started  ", '|', &state->tm_from, 1) < 0)
829 			goto failed;
830 		if (output_ipa_tm("Updated  ", '|', &state->tm_updated, 1) < 0)
831 			goto failed;
832 		if (output("Threshold | %20"PRIu64"\nCounter   | %20"PRIu64
833 		    "\n", state->thr, state->cnt) < 0)
834 			goto failed;
835 		if (output("----------+---------------------\n") < 0)
836 			goto failed;
837 	} else {
838 		if (output(" * No state\n") < 0)
839 			goto failed;
840 	}
841 
842 	return (0);
843 
844 failed:
845 	logbt("output_threshold_stat_raw");
846 	return (-1);
847 }
848 
849 static int
output_thresholds_stat_raw(const struct opt_rule * opt_rule)850 output_thresholds_stat_raw(const struct opt_rule *opt_rule)
851 {
852 	const struct rule *rule;
853 	const struct opt_threshold *opt_threshold;
854 
855 	if (need_nl_raw) {
856 		if (output("\n") < 0)
857 			goto failed;
858 	} else
859 		need_nl_raw = 1;
860 
861 	rule = opt_rule->rule;
862 	if (output("Rule      : %s\nInfo      : %s\n",
863 	    rule->name, rule->info != NULL ? rule->info : "") < 0)
864 		goto failed;
865 
866 	STAILQ_FOREACH(opt_threshold, &opt_rule->opt_thresholds, link)
867 		if (output_threshold_stat_raw(opt_threshold) < 0)
868 			goto failed;
869 
870 	if (output_flush() < 0)
871 		goto failed;
872 
873 	return (0);
874 
875 failed:
876 	logbt("output_thresholds_stat_raw");
877 	return (-1);
878 }
879 
880 static int
show_thresholds_stat(void)881 show_thresholds_stat(void)
882 {
883 	struct rule *rule;
884 	struct threshold *threshold;
885 	struct opt_rule *opt_rule;
886 	struct opt_threshold *opt_threshold;
887 	int rv;
888 
889 	rv = -1;
890 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
891 		rule = opt_rule->rule;
892 		if (rule->info == NULL)
893 			if (st_get_rule_info(rule, &rule->info) < 0)
894 				goto done;
895 		STAILQ_FOREACH(opt_threshold, &opt_rule->opt_thresholds, link) {
896 			threshold = opt_threshold->threshold;
897 			if (threshold->info == NULL)
898 				if (st_get_threshold_info(rule, threshold,
899 				    &threshold->info) < 0)
900 					goto done;
901 			if (st_get_threshold_stat(rule, threshold,
902 			    &opt_threshold->reged, &opt_threshold->state) < 0)
903 				goto done;
904 		}
905 	}
906 
907 	STAILQ_FOREACH(opt_rule, &opt_rules_list, link)
908 		if (output_thresholds_stat_raw(opt_rule) < 0)
909 			goto done;
910 
911 	rv = 0;
912 done:
913 	if (rv != 0)
914 		logbt("show_thresholds_stat");
915 	return (rv);
916 }
917 #endif /* WITH_THRESHOLDS */
918 
919 int
ipastat_main(void)920 ipastat_main(void)
921 {
922 	int rv;
923 
924 	/* Check whether there is any query in -q. */
925 	if (a_flag != A_FLAG_RULES && STAILQ_EMPTY(&opt_rules_list)) {
926 		logmsgx(IPA_LOG_WARNING, "ipastat_main: no query is given");
927 		return (0);
928 	}
929 
930 	if (pre_init_st_mods() < 0)
931 		goto failed;
932 
933 	if (init_rules() < 0)
934 		goto failed;
935 
936 	if (init_st_mods() < 0)
937 		goto failed;
938 
939 	rv = 0;
940 	switch (a_flag) {
941 	case A_FLAG_ABSENT:
942 		switch (cur_opt_rule->type) {
943 		case OPT_RULE_RULE:
944 			if (STAILQ_EMPTY(&opt_tint_list))
945 				opt_tint_init();
946 			rv = show_rules_stat();
947 			break;
948 #ifdef WITH_LIMITS
949 		case OPT_RULE_LIMIT:
950 			rv = show_limits_stat();
951 			break;
952 #endif
953 #ifdef WITH_THRESHOLDS
954 		case OPT_RULE_THRESHOLD:
955 			rv= show_thresholds_stat();
956 			break;
957 #endif
958 		}
959 		break;
960 	case A_FLAG_RULES:
961 		rv = show_rules_list();
962 		break;
963 #ifdef WITH_LIMITS
964 	case A_FLAG_LIMITS:
965 		rv = show_limits_list();
966 		break;
967 #endif
968 #ifdef WITH_THRESHOLDS
969 	case A_FLAG_THRESHOLDS:
970 		rv = show_thresholds_list();
971 		break;
972 #endif
973 	}
974 
975 	if (rv != 0)
976 		goto failed;
977 	return (0);
978 
979 failed:
980 	logbt("ipastat_main");
981 	return (-1);
982 }
983 
984 int
deinit_all(void)985 deinit_all(void)
986 {
987 	int rv;
988 
989 	rv = 0;
990 	if (deinit_rules() < 0)
991 		rv = -1;
992 	if (deinit_st_mods() < 0)
993 		rv = -1;
994 	if (rv != 0)
995 		logbt("deinit_all");
996 	return (rv);
997 }
998 
999 /*
1000  * Free all resources allocated during work and validate internal
1001  * structures after releasing memory.
1002  */
1003 int
free_all(void)1004 free_all(void)
1005 {
1006 	unsigned int n;
1007 	int rv;
1008 
1009 	free_rules();
1010 	if (!STAILQ_EMPTY(&rules_list)) {
1011 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
1012 		    "rules_list is not empty");
1013 		return (-1);
1014 	}
1015 	if (!STAILQ_EMPTY_HEAD_VALID(&rules_list)) {
1016 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
1017 		    "empty rules_list list is in inconsistent state");
1018 		return (-1);
1019 	}
1020 
1021 	n = mzone_nused(rule_mzone);
1022 	if (n != 0) {
1023 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
1024 		    "rule_mzone is not empty: %u", n);
1025 		return (-1);
1026 	}
1027 	mzone_deinit(rule_mzone);
1028 
1029 #ifdef WITH_LIMITS
1030 	n = mzone_nused(limit_mzone);
1031 	if (n != 0) {
1032 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
1033 		    "limit_mzone is not empty: %u", n);
1034 		return (-1);
1035 	}
1036 	mzone_deinit(limit_mzone);
1037 #endif
1038 #ifdef WITH_THRESHOLDS
1039 	n = mzone_nused(threshold_mzone);
1040 	if (n != 0) {
1041 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
1042 		    "threshold_mzone is not empty: %u", n);
1043 		return (-1);
1044 	}
1045 	mzone_deinit(threshold_mzone);
1046 #endif
1047 
1048 	free_rulepats();
1049 
1050 	free_st_lists();
1051 
1052 	memfunc_deinit_1(0);
1053 
1054 	rv = unload_all_mods();
1055 	if (rv < 0)
1056 		logmsgx(IPA_LOG_ERR, "free_all: cannot correctly unload "
1057 		    "all modules");
1058 
1059 	memfunc_deinit_2(0);
1060 	return (rv);
1061 }
1062