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_st.c,v 1.2 2011/01/23 18:42:35 simon Exp $";
32 #endif /* !lint */
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "ipa_mod.h"
39 
40 #include "queue.h"
41 
42 #include "dlapi.h"
43 #include "confcommon.h"
44 #include "memfunc.h"
45 #include "parser.h"
46 
47 #include "ipastat_log.h"
48 #include "ipastat_rules.h"
49 #include "ipastat_st.h"
50 #include "ipastat_main.h"
51 
52 const struct st_list *global_st_list;	/* global { st_list } */
53 
54 signed char	debug_st_null;		/* debug_st_null parameter. */
55 
56 /* Built-in "null" statistics system. */
57 struct st_list st_list_null = STAILQ_HEAD_INITIALIZER(st_list_null);
58 
59 /* List of all used "st_list" parameters. */
60 struct st_sets st_sets = SLIST_HEAD_INITIALIZER(st_sets);
61 
62 /* List of all statistics modules. */
63 struct st_mod_list st_mod_list;
64 
65 /* Current optional statistics name. */
66 struct opt_st *cur_opt_st = NULL;
67 
68 /* List of all opt_st structures. */
69 struct opt_st_list opt_st_list = STAILQ_HEAD_INITIALIZER(opt_st_list);
70 
71 /*
72  * Find a statistics module by configuration prefix.
73  */
74 struct st_mod *
st_mod_by_prefix(const char * prefix)75 st_mod_by_prefix(const char *prefix)
76 {
77 	struct st_mod *st_mod;
78 
79 	SLIST_FOREACH(st_mod, &st_mod_list, link)
80 		if (st_mod->ipa_st_mod->conf_prefix != NULL &&
81 		    strcmp(st_mod->ipa_st_mod->conf_prefix, prefix) == 0)
82 			break;
83 	return (st_mod);
84 }
85 
86 /*
87  * Find a statistics module by a name.
88  */
89 struct st_mod *
st_mod_by_name(const char * name)90 st_mod_by_name(const char *name)
91 {
92 	struct st_mod *st_mod;
93 
94 	SLIST_FOREACH(st_mod, &st_mod_list, link)
95 		if (strcmp(st_mod->ipa_st_mod->st_name, name) == 0)
96 			break;
97 	return (st_mod);
98 }
99 
100 /*
101  * Release memory held by st_set, including struct st_set{}.
102  */
103 static void
free_st_set(struct st_set * set)104 free_st_set(struct st_set *set)
105 {
106 	struct st_elem *st, *st_next;
107 
108 	STAILQ_FOREACH_SAFE(st, &set->list, link, st_next)
109 		mem_free(st, m_anon);
110 	mem_free(set, m_anon);
111 }
112 
113 /*
114  * Release memory held by all st_set.
115  */
116 void
free_st_lists(void)117 free_st_lists(void)
118 {
119 	struct st_set *set, *set_next;
120 
121 	SLIST_FOREACH_SAFE(set, &st_sets, link, set_next)
122 		free_st_set(set);
123 }
124 
125 /*
126  * Pre-initialize all st_mods.
127  */
128 int
pre_init_st_mods(void)129 pre_init_st_mods(void)
130 {
131 	const struct st_mod *st_mod;
132 
133 	SLIST_FOREACH(st_mod, &st_mod_list, link)
134 		if (st_mod->ipa_st_mod->st_pre_init() < 0) {
135 			logmsgx(IPA_LOG_ERR, "module %s: st_pre_init failed",
136 			    st_mod->mod_file);
137 			return (-1);
138 		}
139 	return (0);
140 }
141 
142 /*
143  * Initialize all st_mods.
144  */
145 int
init_st_mods(void)146 init_st_mods(void)
147 {
148 	const struct st_mod *st_mod;
149 
150 	SLIST_FOREACH(st_mod, &st_mod_list, link)
151 		if (st_mod->ipa_st_mod->st_init() < 0) {
152 			logmsgx(IPA_LOG_ERR, "module %s: st_init failed",
153 			    st_mod->mod_file);
154 			return (-1);
155 		}
156 	return (0);
157 }
158 
159 /*
160  * Deinitialize all st_mods.
161  */
162 int
deinit_st_mods(void)163 deinit_st_mods(void)
164 {
165 	const struct st_mod *st_mod;
166 	int rv;
167 
168 	rv = 0;
169 	SLIST_FOREACH(st_mod, &st_mod_list, link)
170 		if (st_mod->ipa_st_mod->st_deinit() < 0) {
171 			logmsgx(IPA_LOG_ERR, "module %s: st_deinit failed",
172 			    st_mod->mod_file);
173 			rv = -1;
174 		}
175 	return (rv);
176 }
177 
178 /*
179  * Initialize one rule in statistics systems.
180  */
181 int
st_init_rule(const struct rule * rule)182 st_init_rule(const struct rule *rule)
183 {
184 	if (!STAILQ_EMPTY(rule->st_list)) {
185 		const struct st_elem *st;
186 
187 		STAILQ_FOREACH(st, rule->st_list, link) {
188 			if (st->ipa_st_mod->st_init_rule == NULL) {
189 				logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
190 				    "st_init_rule: module does not support "
191 				    "rules", st->mod_file, rule->name);
192 				return (-1);
193 			}
194 			if (st->ipa_st_mod->st_init_rule(rule->no,
195 			    rule->name) < 0) {
196 				logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
197 				    "st_init_rule failed", st->mod_file,
198 				    rule->name);
199 				return (-1);
200 			}
201 		}
202 	} else if (debug_st_null)
203 		logdbg("rule %s: uses built-in \"null\" statistics system",
204 		    rule->name);
205 	return (0);
206 }
207 
208 /*
209  * Deinitialize one rule in statistics systems, which it uses.
210  */
211 int
st_deinit_rule(const struct rule * rule)212 st_deinit_rule(const struct rule *rule)
213 {
214 	const struct st_elem *st;
215 	int rv;
216 
217 	rv = 0;
218 	STAILQ_FOREACH(st, rule->st_list, link)
219 		if (st->ipa_st_mod->st_deinit_rule != NULL &&
220 		    st->ipa_st_mod->st_deinit_rule(rule->no) < 0) {
221 			logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
222 			    "st_deinit_rule failed", st->mod_file, rule->name);
223 			rv = -1;
224 		}
225 	return (rv);
226 }
227 
228 int
st_get_rules_list(const struct st_list * st_list,unsigned int * n,struct ipa_entity_desc ** buf_ptr)229 st_get_rules_list(const struct st_list *st_list, unsigned int *n,
230     struct ipa_entity_desc **buf_ptr)
231 {
232 	const struct st_elem *st;
233 
234 	if (STAILQ_EMPTY(st_list)) {
235 		/* "null" system: no records and always successful. */
236 		*n = 0;
237 		*buf_ptr = NULL;
238 		return (0);
239 	}
240 
241 	STAILQ_FOREACH(st, st_list, link)
242 		if (st->ipa_st_mod->st_get_rules_list != NULL) {
243 			if (st->ipa_st_mod->st_get_rules_list(x_pat,
244 			    x_reg_ptr, m_result, n, buf_ptr) == 0)
245 				return (0);
246 			logmsgx(IPA_LOG_ERR, "module %s: "
247 			    "st_get_rules_list failed", st->mod_file);
248 			return (-1);
249 		}
250 	logmsgx(IPA_LOG_ERR, "st_get_rules_list: no statistics system "
251 	    "is able to return rules list");
252 	return (-1);
253 }
254 
255 int
st_get_rule_stat(const struct rule * rule,const ipa_tm * tm1,const ipa_tm * tm2,int exact,unsigned int * n,struct ipa_rule_stat ** buf_ptr)256 st_get_rule_stat(const struct rule *rule, const ipa_tm *tm1, const ipa_tm *tm2,
257     int exact, unsigned int *n, struct ipa_rule_stat **buf_ptr)
258 {
259 	const struct st_elem *st;
260 
261 	if (STAILQ_EMPTY(rule->st_list)) {
262 		/* "null" system: no records and always successful. */
263 		*n = 0;
264 		*buf_ptr = NULL;
265 		return (0);
266 	}
267 
268 	STAILQ_FOREACH(st, rule->st_list, link)
269 		if (st->ipa_st_mod->st_get_rule_stat != NULL) {
270 			if (st->ipa_st_mod->st_get_rule_stat(rule->no, tm1,
271 			    tm2, exact, m_result, n, buf_ptr) == 0)
272 				return (0);
273 			logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
274 			    "st_get_rule_stat failed", st->mod_file,
275 			    rule->name);
276 			return (-1);
277 		}
278 	logmsgx(IPA_LOG_ERR, "rule %s: st_get_rule_stat: no statistics "
279 	    "system is able to return statistics for rule", rule->name);
280 	return (-1);
281 }
282 
283 int
st_get_rule_info(const struct rule * rule,char ** info_ptr)284 st_get_rule_info(const struct rule *rule, char **info_ptr)
285 {
286 	const struct st_elem *st;
287 
288 	if (STAILQ_EMPTY(rule->st_list)) {
289 		/* "null" system: no info. */
290 		*info_ptr = NULL;
291 		return (0);
292 	}
293 
294 	STAILQ_FOREACH(st, rule->st_list, link)
295 		if (st->ipa_st_mod->st_get_rule_info != NULL) {
296 			if (st->ipa_st_mod->st_get_rule_info(rule->no,
297 			    m_result, info_ptr) == 0)
298 				return (0);
299 			logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
300 			    "st_get_rule_info failed", st->mod_file,
301 			    rule->name);
302 			return (-1);
303 		}
304 	logmsgx(IPA_LOG_ERR, "rule %s: st_get_rule_info: no statistics "
305 	    "system is able to return info for rule", rule->name);
306 	return (-1);
307 }
308 
309 #ifdef WITH_LIMITS
310 /*
311  * Initialize one limit in statistics systems.
312  */
313 int
st_init_limit(const struct rule * rule,const struct limit * limit)314 st_init_limit(const struct rule *rule, const struct limit *limit)
315 {
316 	if (!STAILQ_EMPTY(limit->st_list)) {
317 		const struct st_elem *st;
318 
319 		STAILQ_FOREACH(st, limit->st_list, link) {
320 			if (st->ipa_st_mod->st_init_limit == NULL) {
321 				logmsgx(IPA_LOG_ERR, "module %s: rule %s, "
322 				    "limit %s: st_init_limit: module does "
323 				    "not support limits", st->mod_file,
324 				    rule->name, limit->name);
325 				return (-1);
326 			}
327 			if (st->ipa_st_mod->st_init_limit(rule->no, rule->name,
328 			    limit->no, limit->name) < 0) {
329 				logmsgx(IPA_LOG_ERR, "module %s: rule %s, "
330 				    "limit %s: st_init_limit failed",
331 				    st->mod_file, rule->name, limit->name);
332 				return (-1);
333 			}
334 		}
335 	} else if (debug_st_null)
336 		logdbg("rule %s, limit %s: uses built-in \"null\" "
337 		    "statistics system", rule->name, limit->name);
338 	return (0);
339 }
340 
341 /*
342  * Deinitialize one limit in statistics systems, which it uses.
343  */
344 int
st_deinit_limit(const struct rule * rule,const struct limit * limit)345 st_deinit_limit(const struct rule *rule, const struct limit *limit)
346 {
347 	const struct st_elem *st;
348 	int rv;
349 
350 	rv = 0;
351 	STAILQ_FOREACH(st, limit->st_list, link)
352 		if (st->ipa_st_mod->st_deinit_limit != NULL &&
353 		    st->ipa_st_mod->st_deinit_limit(rule->no, limit->no) < 0) {
354 			logmsgx(IPA_LOG_ERR, "module %s: rule %s, limit %s: "
355 			    "st_deinit_limit failed", st->mod_file, rule->name,
356 			    limit->name);
357 			rv = -1;
358 		}
359 	return (rv);
360 }
361 
362 int
st_get_limits_list(const struct rule * rule,unsigned int * n,struct ipa_entity_desc ** buf_ptr)363 st_get_limits_list(const struct rule *rule, unsigned int *n,
364     struct ipa_entity_desc **buf_ptr)
365 {
366 	const struct st_elem *st;
367 
368 	if (STAILQ_EMPTY(rule->st_list)) {
369 		/* "null" system: no records and always successful. */
370 		*n = 0;
371 		*buf_ptr = NULL;
372 		return (0);
373 	}
374 
375 	STAILQ_FOREACH(st, rule->st_list, link)
376 		if (st->ipa_st_mod->st_get_limits_list != NULL) {
377 			if (st->ipa_st_mod->st_get_limits_list(rule->no,
378 			    x_pat, x_reg_ptr, m_result, n, buf_ptr) == 0)
379 				return (0);
380 			logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
381 			    "st_get_limits_list failed", st->mod_file,
382 			    rule->name);
383 			return (-1);
384 		}
385 	logmsgx(IPA_LOG_ERR, "rule %s: st_get_limits_list: no statistics "
386 	    "system is able to return list of limits", rule->name);
387 	return (-1);
388 }
389 
390 int
st_get_limit_stat(const struct rule * rule,const struct limit * limit,const ipa_tm * tm1,const ipa_tm * tm2,unsigned int * n,struct ipa_limit_state ** buf_ptr)391 st_get_limit_stat(const struct rule *rule, const struct limit *limit,
392     const ipa_tm *tm1, const ipa_tm *tm2, unsigned int *n,
393     struct ipa_limit_state **buf_ptr)
394 {
395 	const struct st_elem *st;
396 
397 	if (STAILQ_EMPTY(limit->st_list)) {
398 		/* "null" system: no records and always successful. */
399 		*n = 0;
400 		*buf_ptr = NULL;
401 		return (0);
402 	}
403 
404 	STAILQ_FOREACH(st, limit->st_list, link)
405 		if (st->ipa_st_mod->st_get_limit_stat != NULL) {
406 			if (st->ipa_st_mod->st_get_limit_stat(rule->no,
407 			    limit->no, tm1, tm2, m_result, n, buf_ptr) == 0)
408 				return (0);
409 			logmsgx(IPA_LOG_ERR, "module %s: rule %s, limit %s: "
410 			    "st_get_limit_stat failed", st->mod_file,
411 			    rule->name, limit->name);
412 			return (-1);
413 		}
414 	logmsgx(IPA_LOG_ERR, "rule %s, limit %s: st_get_limit_stat: "
415 	    "no statistics system is able to return statistics for limit",
416 	    rule->name, limit->name);
417 	return (-1);
418 }
419 
420 int
st_get_limit_info(const struct rule * rule,const struct limit * limit,char ** info_ptr)421 st_get_limit_info(const struct rule *rule, const struct limit *limit,
422     char **info_ptr)
423 {
424 	const struct st_elem *st;
425 
426 	if (STAILQ_EMPTY(limit->st_list)) {
427 		/* "null" system: no info. */
428 		*info_ptr = NULL;
429 		return (0);
430 	}
431 
432 	STAILQ_FOREACH(st, limit->st_list, link)
433 		if (st->ipa_st_mod->st_get_limit_info != NULL) {
434 			if (st->ipa_st_mod->st_get_limit_info(rule->no,
435 			    limit->no, m_result, info_ptr) == 0)
436 				return (0);
437 			logmsgx(IPA_LOG_ERR, "module %s: rule %s, limit %s: "
438 			    "st_get_limit_info failed", st->mod_file,
439 			    rule->name, limit->name);
440 			return (-1);
441 		}
442 	logmsgx(IPA_LOG_ERR, "rule %s, limit %s: st_get_limit_info: "
443 	    "no statistics system is able to return info for limit",
444 	    rule->name, limit->name);
445 	return (-1);
446 }
447 #endif /* WITH_LIMITS */
448 
449 #ifdef WITH_THRESHOLDS
450 /*
451  * Initialize one threshold in statistics systems.
452  */
453 int
st_init_threshold(const struct rule * rule,const struct threshold * threshold)454 st_init_threshold(const struct rule *rule, const struct threshold *threshold)
455 {
456 	if (!STAILQ_EMPTY(threshold->st_list)) {
457 		const struct st_elem *st;
458 
459 		STAILQ_FOREACH(st, threshold->st_list, link) {
460 			if (st->ipa_st_mod->st_init_threshold == NULL) {
461 				logmsgx(IPA_LOG_ERR, "module %s: rule %s, "
462 				    "threshold %s: st_init_threshold: "
463 				    "module does not support thresholds",
464 				    st->mod_file, rule->name, threshold->name);
465 				return (-1);
466 			}
467 			if (st->ipa_st_mod->st_init_threshold(rule->no,
468 			    rule->name, threshold->no, threshold->name) < 0) {
469 				logmsgx(IPA_LOG_ERR, "module %s: rule %s, "
470 				    "threshold %s: st_init_threshold failed",
471 				    st->mod_file, rule->name, threshold->name);
472 				return (-1);
473 			}
474 		}
475 	} else if (debug_st_null)
476 		logdbg("rule %s, threshold %s: uses built-in \"null\" "
477 		    "statistics system", rule->name, threshold->name);
478 	return (0);
479 }
480 
481 /*
482  * Deinitialize one threshold in statistics systems, which it uses.
483  */
484 int
st_deinit_threshold(const struct rule * rule,const struct threshold * threshold)485 st_deinit_threshold(const struct rule *rule, const struct threshold *threshold)
486 {
487 	const struct st_elem *st;
488 	int rv;
489 
490 	rv = 0;
491 	STAILQ_FOREACH(st, threshold->st_list, link)
492 		if (st->ipa_st_mod->st_deinit_threshold != NULL &&
493 		    st->ipa_st_mod->st_deinit_threshold(rule->no,
494 		    threshold->no) < 0) {
495 			logmsgx(IPA_LOG_ERR, "module %s: rule %s, "
496 			    "threshold %s: st_deinit_threshold failed",
497 			    st->mod_file, rule->name, threshold->name);
498 			rv = -1;
499 		}
500 	return (rv);
501 }
502 
503 int
st_get_thresholds_list(const struct rule * rule,unsigned int * n,struct ipa_entity_desc ** buf_ptr)504 st_get_thresholds_list(const struct rule *rule, unsigned int *n,
505     struct ipa_entity_desc **buf_ptr)
506 {
507 	const struct st_elem *st;
508 
509 	if (STAILQ_EMPTY(rule->st_list)) {
510 		/* "null" system: no records and always successful. */
511 		*n = 0;
512 		*buf_ptr = NULL;
513 		return (0);
514 	}
515 
516 	STAILQ_FOREACH(st, rule->st_list, link)
517 		if (st->ipa_st_mod->st_get_thresholds_list != NULL) {
518 			if (st->ipa_st_mod->st_get_thresholds_list(rule->no,
519 			    x_pat, x_reg_ptr, m_result, n, buf_ptr) == 0)
520 				return (0);
521 			logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
522 			    "st_get_thresholds_list failed", st->mod_file,
523 			    rule->name);
524 			return (-1);
525 		}
526 	logmsgx(IPA_LOG_ERR, "rule %s: st_get_thresholds_list: no statistics "
527 	    "system is able to return list of thresholds", rule->name);
528 	return (-1);
529 }
530 
531 int
st_get_threshold_stat(const struct rule * rule,const struct threshold * threshold,int * reged,struct ipa_threshold_state * buf)532 st_get_threshold_stat(const struct rule *rule,
533     const struct threshold *threshold, int *reged,
534     struct ipa_threshold_state *buf)
535 {
536 	const struct st_elem *st;
537 	int rv;
538 
539 	if (STAILQ_EMPTY(threshold->st_list)) {
540 		/* "null" system: no statistics for threshold. */
541 		buf->thr = 0;
542 		return (0);
543 	}
544 
545 	STAILQ_FOREACH(st, threshold->st_list, link)
546 		if (st->ipa_st_mod->st_get_threshold_stat != NULL) {
547 			rv = st->ipa_st_mod->st_get_threshold_stat(rule->no,
548 			    threshold->no, buf);
549 			if (rv >= 0) {
550 				*reged = (rv > 0);
551 				return (0);
552 			}
553 			logmsgx(IPA_LOG_ERR, "module %s: rule %s, "
554 			    "threshold %s: st_get_threshold_stat failed",
555 			    st->mod_file, rule->name, threshold->name);
556 			return (-1);
557 		}
558 	logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: st_get_threshold_stat: "
559 	    "no statistics system is able to return statistics for threshold",
560 	    rule->name, threshold->name);
561 	return (-1);
562 }
563 
564 int
st_get_threshold_info(const struct rule * rule,const struct threshold * threshold,char ** info_ptr)565 st_get_threshold_info(const struct rule *rule,
566     const struct threshold *threshold, char **info_ptr)
567 {
568 	const struct st_elem *st;
569 
570 	if (STAILQ_EMPTY(threshold->st_list)) {
571 		/* "null" system: no info. */
572 		*info_ptr = NULL;
573 		return (0);
574 	}
575 
576 	STAILQ_FOREACH(st, threshold->st_list, link)
577 		if (st->ipa_st_mod->st_get_threshold_info != NULL) {
578 			if (st->ipa_st_mod->st_get_threshold_info(rule->no,
579 			    threshold->no, m_result, info_ptr) == 0)
580 				return (0);
581 			logmsgx(IPA_LOG_ERR, "module %s: rule %s, "
582 			    "threshold %s: st_get_threshold_info failed",
583 			    st->mod_file, rule->name, threshold->name);
584 			return (-1);
585 		}
586 	logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: st_get_threshold_info: "
587 	    "no statistics system is able to return info for threshold",
588 	    rule->name, threshold->name);
589 	return (-1);
590 }
591 #endif /* WITH_THRESHOLDS */
592 
593 /*
594  * Add optional statistics name: -q -s name
595  */
596 int
opt_st_add(char * st_names)597 opt_st_add(char *st_names)
598 {
599 	struct opt_st *opt_st;
600 
601 	if (*st_names == '\0') {
602 		cur_opt_st = NULL;
603 		return (0);
604 	}
605 	opt_st = mem_malloc(sizeof(*opt_st), m_anon);
606 	if (opt_st == NULL) {
607 		logmsgx(IPA_LOG_ERR, "opt_st_add: mem_malloc failed");
608 		return (-1);
609 	}
610 	opt_st->st_names = st_names;
611 	STAILQ_INSERT_TAIL(&opt_st_list, opt_st, link);
612 	cur_opt_st = opt_st;
613 	return (0);
614 }
615 
616 /*
617  * Parse names of statistics system given in command query.
618  */
619 int
opt_st_parse(void)620 opt_st_parse(void)
621 {
622 	const struct st_mod *st_mod;
623 	const char *st_name;
624 	struct opt_st *opt_st;
625 	struct st_set *set;
626 	struct st_elem *st;
627 	struct st_list *list;
628 	char *ptr;
629 
630 	STAILQ_FOREACH(opt_st, &opt_st_list, link) {
631 		set = NULL;
632 		list = NULL;
633 
634 		for (ptr = opt_st->st_names; ptr != NULL;) {
635 			st_name = ptr;
636 			ptr = strchr(ptr, ' ');
637 			if (ptr != NULL) {
638 				*ptr++ = '\0';
639 				for (; *ptr == ' '; ++ptr)
640 					;
641 			}
642 			/* Handle "null" statistics system. */
643 			if (strcmp(st_name, "null") == 0) {
644 				if (list == NULL && ptr == NULL) {
645 					list = &st_list_null;
646 					continue;
647 				}
648 				logmsgx(IPA_LOG_ERR, "opt_st_parse: built-in "
649 				    "statistics system \"null\" cannot be used "
650 				    "together with another statistics systems");
651 				return (-1);
652 			}
653 
654 			st_mod = st_mod_by_name(st_name);
655 			if (st_mod == NULL) {
656 				logmsgx(IPA_LOG_ERR, "opt_st_parse: "
657 				    "cannot find module with \"%s\" statistics "
658 				    "system name", st_name);
659 				return (-1);
660 			}
661 
662 			if (set != NULL) {
663 				/* We already have set. */
664 				STAILQ_FOREACH(st, list, link)
665 					if (strcmp(st_name,
666 					    st->ipa_st_mod->st_name) == 0) {
667 						logmsgx(IPA_LOG_ERR,
668 						    "opt_st_parse: duplicated "
669 						    "statistics system name "
670 						    "\"%s\"", st_name);
671 						return (-1);
672 					}
673 			} else {
674 				/* Create new set for st_list parameter. */
675 				set = mem_malloc(sizeof(*set), m_anon);
676 				if (set == NULL) {
677 					logmsgx(IPA_LOG_ERR, "opt_st_parse: "
678 					    "mem_malloc failed");
679 					return (-1);
680 				}
681 				list = &set->list;
682 				STAILQ_INIT(list);
683 			}
684 
685 			/* Add new st element to st_list. */
686 			st = mem_malloc(sizeof(*st), m_anon);
687 			if (st == NULL) {
688 				logmsgx(IPA_LOG_ERR, "opt_st_parse: "
689 				    "mem_malloc failed");
690 				return (-1);
691 			}
692 			st->ipa_st_mod = st_mod->ipa_st_mod;
693 			st->mod_file = st_mod->mod_file;
694 
695 			STAILQ_INSERT_TAIL(list, st, link);
696 		}
697 
698 		opt_st->st_list = list;
699 		if (set != NULL)
700 			SLIST_INSERT_HEAD(&st_sets, set, link);
701 	}
702 
703 	return (0);
704 }
705 
706 /*
707  * Release memory previously allocated by all opt_st_add() calls.
708  */
709 void
opt_st_free(void)710 opt_st_free(void)
711 {
712 	struct opt_st *opt_st, *opt_st_next;
713 
714 	STAILQ_FOREACH_SAFE(opt_st, &opt_st_list, link, opt_st_next)
715 		mem_free(opt_st, m_anon);
716 }
717