root/src/noit_check.c

Revision ab14a2b0fe3e65dbff2d748604e9f2fabb279844, 59.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 weeks ago)

clear the status and metrics if they exist

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
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 are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <ctype.h>
39 #include <assert.h>
40 #include <errno.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <time.h>
44
45 #include "dtrace_probes.h"
46 #include "utils/noit_log.h"
47 #include "utils/noit_hash.h"
48 #include "utils/noit_skiplist.h"
49 #include "noit_conf.h"
50 #include "noit_check.h"
51 #include "noit_module.h"
52 #include "noit_console.h"
53 #include "noit_check_tools.h"
54 #include "noit_check_resolver.h"
55 #include "eventer/eventer.h"
56
57 #define DEFAULT_TEXT_METRIC_SIZE_LIMIT  512
58 #define RECYCLE_INTERVAL 60
59
60 NOIT_HOOK_IMPL(check_stats_set_metric,
61   (noit_check_t *check, stats_t *stats, metric_t *m),
62   void *, closure,
63   (void *closure, noit_check_t *check, stats_t *stats, metric_t *m),
64   (closure,check,stats,m))
65
66 NOIT_HOOK_IMPL(check_stats_set_metric_coerce,
67   (noit_check_t *check, stats_t *stats, const char *name,
68    metric_type_t type, const char *v, noit_boolean success),
69   void *, closure,
70   (void *closure, noit_check_t *check, stats_t *stats, const char *name,
71    metric_type_t type, const char *v, noit_boolean success),
72   (closure,check,stats,name,type,v,success))
73
74 NOIT_HOOK_IMPL(check_passive_log_stats,
75   (noit_check_t *check),
76   void *, closure,
77   (void *closure, noit_check_t *check),
78   (closure,check))
79
80 NOIT_HOOK_IMPL(check_log_stats,
81   (noit_check_t *check),
82   void *, closure,
83   (void *closure, noit_check_t *check),
84   (closure,check))
85
86 /* 20 ms slots over 60 second for distribution */
87 #define SCHEDULE_GRANULARITY 20
88 #define SLOTS_PER_SECOND (1000/SCHEDULE_GRANULARITY)
89 #define MAX_MODULE_REGISTRATIONS 64
90
91 /* used to manage per-check generic module metadata */
92 struct vp_w_free {
93   void *ptr;
94   void (*freefunc)(void *);
95 };
96
97 static int text_size_limit = DEFAULT_TEXT_METRIC_SIZE_LIMIT;
98 static int reg_module_id = 0;
99 static char *reg_module_names[MAX_MODULE_REGISTRATIONS] = { NULL };
100 static int reg_module_used = -1;
101 static u_int64_t check_completion_count = 0ULL;
102 static u_int64_t check_metrics_seen = 0ULL;
103 static noit_hash_table polls = NOIT_HASH_EMPTY;
104 static noit_skiplist watchlist = { 0 };
105 static noit_skiplist polls_by_name = { 0 };
106 static u_int32_t __config_load_generation = 0;
107 static unsigned short check_slots_count[60000 / SCHEDULE_GRANULARITY] = { 0 },
108                       check_slots_seconds_count[60] = { 0 };
109
110 static int
111 noit_console_show_timing_slots(noit_console_closure_t ncct,
112                                int argc, char **argv,
113                                noit_console_state_t *dstate,
114                                void *closure) {
115   int i, j;
116   const int upl = (60000 / SCHEDULE_GRANULARITY) / 60;
117   for(i=0;i<60;i++) {
118     nc_printf(ncct, "[%02d] %04d: ", i, check_slots_seconds_count[i]);
119     for(j=i*upl;j<(i+1)*upl;j++) {
120       char cp = '!';
121       if(check_slots_count[j] < 10) cp = '0' + check_slots_count[j];
122       else if(check_slots_count[j] < 36) cp = 'a' + (check_slots_count[j] - 10);
123       nc_printf(ncct, "%c", cp);
124     }
125     nc_printf(ncct, "\n");
126   }
127   return 0;
128 }
129 static int
130 noit_check_add_to_list(noit_check_t *new_check, const char *newname) {
131   if(!(new_check->flags & NP_TRANSIENT)) {
132     assert(new_check->name || newname);
133     /* This remove could fail -- no big deal */
134     if(new_check->name != NULL)
135       noit_skiplist_remove(&polls_by_name, new_check, NULL);
136
137     /* optional update the name (at the critical point) */
138     if(newname) {
139       if(new_check->name) free(new_check->name);
140       new_check->name = strdup(newname);
141     }
142
143     /* This insert could fail.. which means we have a conflict on
144      * target`name.  That should result in the check being disabled. */
145     if(!noit_skiplist_insert(&polls_by_name, new_check)) {
146       noitL(noit_error, "Check %s`%s disabled due to naming conflict\n",
147             new_check->target, new_check->name);
148       new_check->flags |= NP_DISABLED;
149     }
150   }
151   return 1;
152 }
153
154 u_int64_t noit_check_metric_count() {
155   return check_metrics_seen;
156 }
157 void noit_check_metric_count_add(int add) {
158   noit_atomic64_t *n = (noit_atomic64_t *)&check_metrics_seen;
159   noit_atomic64_t v = (noit_atomic64_t)add;
160   noit_atomic_add64(n, v);
161 }
162
163 u_int64_t noit_check_completion_count() {
164   return check_completion_count;
165 }
166 static void register_console_check_commands();
167 static int check_recycle_bin_processor(eventer_t, int, void *,
168                                        struct timeval *);
169
170 static int
171 check_slots_find_smallest(int sec) {
172   int i, j, cyclic, random_offset, jbase = 0, mini = 0, minj = 0;
173   unsigned short min_running_i = 0xffff, min_running_j = 0xffff;
174   for(i=0;i<60;i++) {
175     int adj_i = (i + sec) % 60;
176     if(check_slots_seconds_count[adj_i] < min_running_i) {
177       min_running_i = check_slots_seconds_count[adj_i];
178       mini = adj_i;
179     }
180   }
181   jbase = mini * (1000/SCHEDULE_GRANULARITY);
182   random_offset = drand48() * SLOTS_PER_SECOND;
183   for(cyclic=0;cyclic<SLOTS_PER_SECOND;cyclic++) {
184     j = jbase + ((random_offset + cyclic) % SLOTS_PER_SECOND);
185     if(check_slots_count[j] < min_running_j) {
186       min_running_j = check_slots_count[j];
187       minj = j;
188     }
189   }
190   return (minj * SCHEDULE_GRANULARITY) + drand48() * SCHEDULE_GRANULARITY;
191 }
192 static void
193 check_slots_adjust_tv(struct timeval *tv, short adj) {
194   int offset_ms, idx;
195   offset_ms = (tv->tv_sec % 60) * 1000 + (tv->tv_usec / 1000);
196   idx = offset_ms / SCHEDULE_GRANULARITY;
197   check_slots_count[idx] += adj;
198   check_slots_seconds_count[offset_ms / 1000] += adj;
199 }
200 void check_slots_inc_tv(struct timeval *tv) {
201   check_slots_adjust_tv(tv, 1);
202 }
203 void check_slots_dec_tv(struct timeval *tv) {
204   check_slots_adjust_tv(tv, -1);
205 }
206 const char *
207 noit_check_available_string(int16_t available) {
208   switch(available) {
209     case NP_AVAILABLE:    return "available";
210     case NP_UNAVAILABLE:  return "unavailable";
211     case NP_UNKNOWN:      return "unknown";
212   }
213   return NULL;
214 }
215 const char *
216 noit_check_state_string(int16_t state) {
217   switch(state) {
218     case NP_GOOD:         return "good";
219     case NP_BAD:          return "bad";
220     case NP_UNKNOWN:      return "unknown";
221   }
222   return NULL;
223 }
224 static int __check_name_compare(const void *a, const void *b) {
225   const noit_check_t *ac = a;
226   const noit_check_t *bc = b;
227   int rv;
228   if((rv = strcmp(ac->target, bc->target)) != 0) return rv;
229   if((rv = strcmp(ac->name, bc->name)) != 0) return rv;
230   return 0;
231 }
232 static int __watchlist_compare(const void *a, const void *b) {
233   const noit_check_t *ac = a;
234   const noit_check_t *bc = b;
235   int rv;
236   if((rv = memcmp(ac->checkid, bc->checkid, sizeof(ac->checkid))) != 0) return rv;
237   if(ac->period < bc->period) return -1;
238   if(ac->period == bc->period) return 0;
239   return 1;
240 }
241 static int __check_target_ip_compare(const void *a, const void *b) {
242   const noit_check_t *ac = a;
243   const noit_check_t *bc = b;
244   int rv;
245   if((rv = strcmp(ac->target_ip, bc->target_ip)) != 0) return rv;
246   if (ac->name == NULL) return 1;
247   if (bc->name == NULL) return -1;
248   if((rv = strcmp(ac->name, bc->name)) != 0) return rv;
249   return 1;
250 }
251 static int __check_target_compare(const void *a, const void *b) {
252   const noit_check_t *ac = a;
253   const noit_check_t *bc = b;
254   int rv;
255   if (ac->target == NULL) return 1;
256   if (bc->target == NULL) return -1;
257   if((rv = strcmp(ac->target, bc->target)) != 0) return rv;
258   if (ac->name == NULL) return 1;
259   if (bc->name == NULL) return -1;
260   if((rv = strcmp(ac->name, bc->name)) != 0) return rv;
261   return 1;
262 }
263 int
264 noit_calc_rtype_flag(char *resolve_rtype) {
265   int flags = 0;
266   if(resolve_rtype) {
267     flags |= strcmp(resolve_rtype, PREFER_IPV6) == 0 ||
268              strcmp(resolve_rtype, FORCE_IPV6) == 0 ? NP_PREFER_IPV6 : 0;
269     flags |= strcmp(resolve_rtype, FORCE_IPV4) == 0 ||
270              strcmp(resolve_rtype, FORCE_IPV6) == 0 ? NP_SINGLE_RESOLVE : 0;
271   }
272   return flags;
273 }
274 void
275 noit_check_fake_last_check(noit_check_t *check,
276                            struct timeval *lc, struct timeval *_now) {
277   struct timeval now, period;
278   int balance_ms;
279
280   if(!_now) {
281     gettimeofday(&now, NULL);
282     _now = &now;
283   }
284   period.tv_sec = check->period / 1000;
285   period.tv_usec = (check->period % 1000) * 1000;
286   sub_timeval(*_now, period, lc);
287
288   if(!(check->flags & NP_TRANSIENT) && check->period) {
289     balance_ms = check_slots_find_smallest(_now->tv_sec+1);
290     lc->tv_sec = (lc->tv_sec / 60) * 60 + balance_ms / 1000;
291     lc->tv_usec = (balance_ms % 1000) * 1000;
292     if(compare_timeval(*_now, *lc) < 0) {
293       do {
294         sub_timeval(*lc, period, lc);
295       } while(compare_timeval(*_now, *lc) < 0);
296     }
297     else {
298       struct timeval test;
299       while(1) {
300         add_timeval(*lc, period, &test);
301         if(compare_timeval(*_now, test) < 0) break;
302         memcpy(lc, &test, sizeof(test));
303       }
304     }
305   }
306   /* now, we're going to do an even distribution using the slots */
307   if(!(check->flags & NP_TRANSIENT)) check_slots_inc_tv(lc);
308 }
309 void
310 noit_poller_process_checks(const char *xpath) {
311   int i, flags, cnt = 0;
312   noit_conf_section_t *sec;
313   __config_load_generation++;
314   sec = noit_conf_get_sections(NULL, xpath, &cnt);
315   for(i=0; i<cnt; i++) {
316     void *vcheck;
317     char uuid_str[37];
318     char target[256] = "";
319     char module[256] = "";
320     char name[256] = "";
321     char filterset[256] = "";
322     char oncheck[1024] = "";
323     char resolve_rtype[16] = "";
324     int ridx;
325     int no_period = 0;
326     int no_oncheck = 0;
327     int period = 0, timeout = 0;
328     noit_boolean disabled = noit_false, busted = noit_false;
329     uuid_t uuid, out_uuid;
330     noit_hash_table *options;
331     noit_hash_table **moptions = NULL;
332     noit_boolean moptions_used = noit_false;
333
334     if(reg_module_id > 0) {
335       moptions = alloca(reg_module_id * sizeof(noit_hash_table *));
336       memset(moptions, 0, reg_module_id * sizeof(noit_hash_table *));
337       moptions_used = noit_true;
338     }
339
340 #define NEXT(...) noitL(noit_stderr, __VA_ARGS__); continue
341 #define MYATTR(type,a,...) noit_conf_get_##type(sec[i], "@" #a, __VA_ARGS__)
342 #define INHERIT(type,a,...) \
343   noit_conf_get_##type(sec[i], "ancestor-or-self::node()/@" #a, __VA_ARGS__)
344
345     if(!MYATTR(stringbuf, uuid, uuid_str, sizeof(uuid_str))) {
346       noitL(noit_stderr, "check %d has no uuid\n", i+1);
347       continue;
348     }
349
350     if(uuid_parse(uuid_str, uuid)) {
351       noitL(noit_stderr, "check uuid: '%s' is invalid\n", uuid_str);
352       continue;
353     }
354
355     if(!INHERIT(stringbuf, target, target, sizeof(target))) {
356       noitL(noit_stderr, "check uuid: '%s' has no target\n", uuid_str);
357       busted = noit_true;
358     }
359     if(!INHERIT(stringbuf, module, module, sizeof(module))) {
360       noitL(noit_stderr, "check uuid: '%s' has no module\n", uuid_str);
361       busted = noit_true;
362     }
363
364     if(!INHERIT(stringbuf, filterset, filterset, sizeof(filterset)))
365       filterset[0] = '\0';
366    
367     if (!INHERIT(stringbuf, resolve_rtype, resolve_rtype, sizeof(resolve_rtype)))
368       strlcpy(resolve_rtype, PREFER_IPV4, sizeof(resolve_rtype));
369
370     if(!MYATTR(stringbuf, name, name, sizeof(name)))
371       strlcpy(name, module, sizeof(name));
372
373     if(!INHERIT(int, period, &period) || period == 0)
374       no_period = 1;
375
376     if(!INHERIT(stringbuf, oncheck, oncheck, sizeof(oncheck)) || !oncheck[0])
377       no_oncheck = 1;
378
379     if(no_period && no_oncheck) {
380       noitL(noit_stderr, "check uuid: '%s' has neither period nor oncheck\n",
381             uuid_str);
382       busted = noit_true;
383     }
384     if(!(no_period || no_oncheck)) {
385       noitL(noit_stderr, "check uuid: '%s' has oncheck and period.\n",
386             uuid_str);
387       busted = noit_true;
388     }
389     if(!INHERIT(int, timeout, &timeout)) {
390       noitL(noit_stderr, "check uuid: '%s' has no timeout\n", uuid_str);
391       busted = noit_true;
392     }
393     if(!no_period && timeout >= period) {
394       noitL(noit_stderr, "check uuid: '%s' timeout > period\n", uuid_str);
395       timeout = period/2;
396     }
397     options = noit_conf_get_hash(sec[i], "config");
398     for(ridx=0; ridx<reg_module_id; ridx++) {
399       moptions[ridx] = noit_conf_get_namespaced_hash(sec[i], "config",
400                                                      reg_module_names[ridx]);
401     }
402
403     INHERIT(boolean, disable, &disabled);
404     flags = 0;
405     if(busted) flags |= (NP_UNCONFIG|NP_DISABLED);
406     else if(disabled) flags |= NP_DISABLED;
407
408     flags |= noit_calc_rtype_flag(resolve_rtype);
409
410     if(noit_hash_retrieve(&polls, (char *)uuid, UUID_SIZE,
411                           &vcheck)) {
412       noit_check_t *existing_check = (noit_check_t *)vcheck;
413       /* Once set, it cannot be checked if the check is live */
414       assert(!existing_check->module || !existing_check->module[0] ||
415              !strcmp(existing_check->module, module) ||
416              !NOIT_CHECK_LIVE(existing_check));
417       /* Set it if it is unset or being changed */
418       if(!existing_check->module || !existing_check->module[0] ||
419          strcmp(existing_check->module, module)) {
420         if(existing_check->module) free(existing_check->module);
421         existing_check->module = strdup(module);
422       }
423       noit_check_update(existing_check, target, name, filterset, options,
424                            moptions_used ? moptions : NULL,
425                            period, timeout, oncheck[0] ? oncheck : NULL,
426                            flags);
427       noitL(noit_debug, "reloaded uuid: %s\n", uuid_str);
428     }
429     else {
430       noit_poller_schedule(target, module, name, filterset, options,
431                            moptions_used ? moptions : NULL,
432                            period, timeout, oncheck[0] ? oncheck : NULL,
433                            flags, uuid, out_uuid);
434       noitL(noit_debug, "loaded uuid: %s\n", uuid_str);
435     }
436
437     for(ridx=0; ridx<reg_module_id; ridx++) {
438       if(moptions[ridx]) {
439         noit_hash_destroy(moptions[ridx], free, free);
440         free(moptions[ridx]);
441       }
442     }
443     noit_hash_destroy(options, free, free);
444     free(options);
445   }
446   if(sec) free(sec);
447 }
448
449 int
450 noit_check_activate(noit_check_t *check) {
451   noit_module_t *mod;
452   if(NOIT_CHECK_LIVE(check)) return 0;
453   mod = noit_module_lookup(check->module);
454   if(mod && mod->initiate_check) {
455     if((check->flags & NP_DISABLED) == 0) {
456       mod->initiate_check(mod, check, 0, NULL);
457       return 1;
458     }
459     else
460       noitL(noit_debug, "Skipping %s`%s, disabled.\n",
461             check->target, check->name);
462   }
463   else {
464     if(!mod) {
465       noitL(noit_stderr, "Cannot find module '%s'\n", check->module);
466       check->flags |= NP_DISABLED;
467     }
468   }
469   return 0;
470 }
471
472 void
473 noit_poller_initiate() {
474   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
475   uuid_t key_id;
476   int klen;
477   void *vcheck;
478   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
479                        &vcheck)) {
480     noit_check_activate((noit_check_t *)vcheck);
481   }
482 }
483
484 void
485 noit_poller_flush_epoch(int oldest_allowed) {
486   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
487   uuid_t key_id;
488   int klen;
489   noit_check_t *tofree = NULL;
490   void *vcheck;
491
492   /* Cleanup any previous causal map */
493   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
494                        &vcheck)) {
495     noit_check_t *check = (noit_check_t *)vcheck;
496     /* We don't free the one we're looking at... we free it on the next
497      * pass.  This leaves out iterator in good shape.  We just need to
498      * remember to free it one last time outside the while loop, down...
499      */
500     if(tofree) {
501       noit_poller_deschedule(tofree->checkid);
502       tofree = NULL;
503     }
504     if(check->generation < oldest_allowed) {
505       tofree = check;
506     }
507   }
508   /* ... here */
509   if(tofree) noit_poller_deschedule(tofree->checkid);
510 }
511
512 void
513 noit_poller_make_causal_map() {
514   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
515   uuid_t key_id;
516   int klen;
517   void *vcheck;
518
519   /* Cleanup any previous causal map */
520   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
521                        &vcheck)) {
522     noit_check_t *check = (noit_check_t *)vcheck;
523     dep_list_t *dep;
524     while((dep = check->causal_checks) != NULL) {
525       check->causal_checks = dep->next;
526       free(dep);
527     }
528   }
529
530   memset(&iter, 0, sizeof(iter));
531   /* Walk all checks and add check dependencies to their parents */
532   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
533                        &vcheck)) {
534     noit_check_t *check = (noit_check_t *)vcheck, *parent;
535     if(check->oncheck) {
536       /* This service is causally triggered by another service */
537       uuid_t id;
538       char fullcheck[1024];
539       char *name = check->oncheck;
540       char *target = NULL;
541
542       noitL(noit_debug, "Searching for upstream trigger on %s\n", name);
543       parent = NULL;
544       if(uuid_parse(check->oncheck, id) == 0) {
545         target = "";
546         parent = noit_poller_lookup(id);
547       }
548       else if((target = strchr(check->oncheck, '`')) != NULL) {
549         strlcpy(fullcheck, check->oncheck, target + 1 - check->oncheck);
550         name = target + 1;
551         target = fullcheck;
552         parent = noit_poller_lookup_by_name(target, name);
553       }
554       else {
555         target = check->target;
556         parent = noit_poller_lookup_by_name(target, name);
557       }
558
559       if(!parent) {
560         check->flags |= NP_DISABLED;
561         noitL(noit_stderr, "Disabling check %s`%s, can't find oncheck %s`%s\n",
562               check->target, check->name, target, name);
563       }
564       else {
565         dep_list_t *dep;
566         dep = malloc(sizeof(*dep));
567         dep->check = check;
568         dep->next = parent->causal_checks;
569         parent->causal_checks = dep;
570         noitL(noit_debug, "Causal map %s`%s --> %s`%s\n",
571               parent->target, parent->name, check->target, check->name);
572       }
573     }
574   }
575 }
576 void
577 noit_poller_reload(const char *xpath)
578 {
579   noit_poller_process_checks(xpath ? xpath : "/noit/checks//check");
580   if(!xpath) {
581     /* Full reload, we need to wipe old checks */
582     noit_poller_flush_epoch(__config_load_generation);
583   }
584   noit_poller_make_causal_map();
585   noit_poller_initiate();
586 }
587 void
588 noit_poller_init() {
589   srand48((getpid() << 16) ^ time(NULL));
590   noit_check_resolver_init();
591   noit_check_tools_init();
592   noit_skiplist_init(&polls_by_name);
593   noit_skiplist_set_compare(&polls_by_name, __check_name_compare,
594                             __check_name_compare);
595   noit_skiplist_add_index(&polls_by_name, __check_target_ip_compare,
596                             __check_target_ip_compare);
597   noit_skiplist_add_index(&polls_by_name, __check_target_compare,
598                             __check_target_compare);
599   noit_skiplist_init(&watchlist);
600   noit_skiplist_set_compare(&watchlist, __watchlist_compare,
601                             __watchlist_compare);
602   register_console_check_commands();
603   eventer_name_callback("check_recycle_bin_processor",
604                         check_recycle_bin_processor);
605   eventer_add_in_s_us(check_recycle_bin_processor, NULL, RECYCLE_INTERVAL, 0);
606   noit_conf_get_int(NULL, "noit/@text_size_limit", &text_size_limit);
607   if (text_size_limit <= 0) {
608     text_size_limit = DEFAULT_TEXT_METRIC_SIZE_LIMIT;
609   }
610   noit_poller_reload(NULL);
611 }
612
613 int
614 noit_poller_check_count() {
615   return polls_by_name.size;
616 }
617
618 int
619 noit_poller_transient_check_count() {
620   return watchlist.size;
621 }
622
623 noit_check_t *
624 noit_check_clone(uuid_t in) {
625   int i;
626   noit_check_t *checker, *new_check;
627   void *vcheck;
628   if(noit_hash_retrieve(&polls,
629                         (char *)in, UUID_SIZE,
630                         &vcheck) == 0) {
631     return NULL;
632   }
633   checker = (noit_check_t *)vcheck;
634   if(checker->oncheck) {
635     return NULL;
636   }
637   new_check = calloc(1, sizeof(*new_check));
638   memcpy(new_check, checker, sizeof(*new_check));
639   new_check->target = strdup(new_check->target);
640   new_check->module = strdup(new_check->module);
641   new_check->name = strdup(new_check->name);
642   new_check->filterset = strdup(new_check->filterset);
643   new_check->flags = 0;
644   new_check->fire_event = NULL;
645   memset(&new_check->last_fire_time, 0, sizeof(new_check->last_fire_time));
646   memset(&new_check->stats, 0, sizeof(new_check->stats));
647   new_check->closure = NULL;
648   new_check->config = calloc(1, sizeof(*new_check->config));
649   noit_hash_merge_as_dict(new_check->config, checker->config);
650   new_check->module_configs = NULL;
651   new_check->module_metadata = NULL;
652
653   for(i=0; i<reg_module_id; i++) {
654     void *src_metadata;
655     noit_hash_table *src_mconfig;
656     src_mconfig = noit_check_get_module_config(checker, i);
657     if(src_mconfig) {
658       noit_hash_table *t = calloc(1, sizeof(*new_check->config));
659       noit_hash_merge_as_dict(t, src_mconfig);
660       noit_check_set_module_config(new_check, i, t);
661     }
662     if(checker->flags & NP_PASSIVE_COLLECTION)
663       if(NULL != (src_metadata = noit_check_get_module_metadata(new_check, i)))
664         noit_check_set_module_metadata(new_check, i, src_metadata, NULL);
665   }
666   return new_check;
667 }
668
669 noit_check_t *
670 noit_check_watch(uuid_t in, int period) {
671   /* First look for a copy that is being watched */
672   int minimum_pi = 1000, granularity_pi = 500;
673   noit_conf_section_t check_node;
674   char uuid_str[UUID_STR_LEN + 1];
675   char xpath[1024];
676   noit_check_t n, *f;
677
678   uuid_unparse_lower(in, uuid_str);
679
680   noitL(noit_debug, "noit_check_watch(%s,%d)\n", uuid_str, period);
681   if(period == 0) {
682     return noit_poller_lookup(in);
683   }
684
685   /* Find the check */
686   snprintf(xpath, sizeof(xpath), "//checks//check[@uuid=\"%s\"]", uuid_str);
687   check_node = noit_conf_get_section(NULL, xpath);
688   noit_conf_get_int(NULL, "//checks/@transient_min_period", &minimum_pi);
689   noit_conf_get_int(NULL, "//checks/@transient_period_granularity", &granularity_pi);
690   if(check_node) {
691     noit_conf_get_int(check_node,
692                       "ancestor-or-self::node()/@transient_min_period",
693                       &minimum_pi);
694     noit_conf_get_int(check_node,
695                       "ancestor-or-self::node()/@transient_period_granularity",
696                       &granularity_pi);
697   }
698
699   /* apply the bounds */
700   period /= granularity_pi;
701   period *= granularity_pi;
702   period = MAX(period, minimum_pi);
703
704   uuid_copy(n.checkid, in);
705   n.period = period;
706
707   f = noit_skiplist_find(&watchlist, &n, NULL);
708   if(f) return f;
709   f = noit_check_clone(in);
710   if(!f) return NULL;
711   f->period = period;
712   f->timeout = period - 10;
713   f->flags |= NP_TRANSIENT;
714   noitL(noit_debug, "Watching %s@%d\n", uuid_str, period);
715   noit_skiplist_insert(&watchlist, f);
716   return f;
717 }
718
719 noit_check_t *
720 noit_check_get_watch(uuid_t in, int period) {
721   noit_check_t n, *f;
722
723   uuid_copy(n.checkid, in);
724   n.period = period;
725
726   f = noit_skiplist_find(&watchlist, &n, NULL);
727   return f;
728 }
729
730 void
731 noit_check_transient_add_feed(noit_check_t *check, const char *feed) {
732   char *feedcopy;
733   if(!check->feeds) {
734     check->feeds = calloc(1, sizeof(*check->feeds));
735     noit_skiplist_init(check->feeds);
736     noit_skiplist_set_compare(check->feeds,
737                               (noit_skiplist_comparator_t)strcmp,
738                               (noit_skiplist_comparator_t)strcmp);
739   }
740   feedcopy = strdup(feed);
741   /* No error on failure -- it's already there */
742   if(noit_skiplist_insert(check->feeds, feedcopy) == NULL) free(feedcopy);
743   noitL(noit_debug, "check %s`%s @ %dms has %d feed(s): %s.\n",
744         check->target, check->name, check->period, check->feeds->size, feed);
745 }
746 void
747 noit_check_transient_remove_feed(noit_check_t *check, const char *feed) {
748   if(!check->feeds) return;
749   if(feed) {
750     noitL(noit_debug, "check %s`%s @ %dms removing 1 of %d feeds: %s.\n",
751           check->target, check->name, check->period, check->feeds->size, feed);
752     noit_skiplist_remove(check->feeds, feed, free);
753   }
754   if(check->feeds->size == 0) {
755     char uuid_str[UUID_STR_LEN + 1];
756     uuid_unparse_lower(check->checkid, uuid_str);
757     noitL(noit_debug, "Unwatching %s@%d\n", uuid_str, check->period);
758     noit_skiplist_remove(&watchlist, check, NULL);
759     noit_skiplist_destroy(check->feeds, free);
760     free(check->feeds);
761     check->feeds = NULL;
762     if(check->flags & NP_TRANSIENT) {
763       noitL(noit_debug, "check %s`%s @ %dms has no more listeners.\n",
764             check->target, check->name, check->period);
765       check->flags |= NP_KILLED;
766     }
767     noit_poller_free_check(check);
768   }
769 }
770
771 noit_boolean
772 noit_check_is_valid_target(const char *target) {
773   int8_t family;
774   int rv;
775   union {
776     struct in_addr addr4;
777     struct in6_addr addr6;
778   } a;
779
780   family = AF_INET;
781   rv = inet_pton(family, target, &a);
782   if(rv != 1) {
783     family = AF_INET6;
784     rv = inet_pton(family, target, &a);
785     if(rv != 1) {
786       return noit_false;
787     }
788   }
789   return noit_true;
790 }
791 int
792 noit_check_set_ip(noit_check_t *new_check,
793                   const char *ip_str, const char *newname) {
794   int8_t family;
795   int rv, failed = 0;
796   char old_target_ip[INET6_ADDRSTRLEN];
797   union {
798     struct in_addr addr4;
799     struct in6_addr addr6;
800   } a;
801
802   memset(old_target_ip, 0, INET6_ADDRSTRLEN);
803   strlcpy(old_target_ip, new_check->target_ip, sizeof(old_target_ip));
804
805   family = NOIT_CHECK_PREFER_V6(new_check) ? AF_INET6 : AF_INET;
806   rv = inet_pton(family, ip_str, &a);
807   if(rv != 1) {
808     if (!NOIT_CHECK_SINGLE_RESOLVE(new_check)) {
809       family = family == AF_INET ? AF_INET6 : AF_INET;
810       rv = inet_pton(family, ip_str, &a);
811       if(rv != 1) {
812         family = AF_INET;
813         memset(&a, 0, sizeof(a));
814         failed = -1;
815       }
816     } else {
817       failed = -1;
818     }
819   }
820
821   new_check->target_family = family;
822   memcpy(&new_check->target_addr, &a, sizeof(a));
823   new_check->target_ip[0] = '\0';
824   if(failed == 0)
825     if(inet_ntop(new_check->target_family,
826                  &new_check->target_addr,
827                  new_check->target_ip,
828                  sizeof(new_check->target_ip)) == NULL) {
829       noitL(noit_error, "inet_ntop failed [%s] -> %d\n", ip_str, errno);
830     }
831   /*
832    * new_check->name could be null if this check is being set for the
833    * first time.  add_to_list will set it.
834    */
835   if (new_check->name == NULL ||
836       strcmp(old_target_ip, new_check->target_ip) != 0) {
837     noit_check_add_to_list(new_check, newname);
838   }
839
840   if(new_check->name == NULL && newname != NULL) {
841     assert(new_check->flags & NP_TRANSIENT);
842     new_check->name = strdup(newname);
843   }
844
845   return failed;
846 }
847 int
848 noit_check_resolve(noit_check_t *check) {
849   uint8_t family_pref = NOIT_CHECK_PREFER_V6(check) ? AF_INET6 : AF_INET;
850   char ipaddr[INET6_ADDRSTRLEN];
851   if(!NOIT_CHECK_SHOULD_RESOLVE(check)) return 1; /* success, not required */
852   noit_check_resolver_remind(check->target);
853   if(noit_check_resolver_fetch(check->target, ipaddr, sizeof(ipaddr),
854                                family_pref) >= 0) {
855     check->flags |= NP_RESOLVED;
856     noit_check_set_ip(check, ipaddr, NULL);
857     return 0;
858   }
859   check->flags &= ~NP_RESOLVED;
860   return -1;
861 }
862 int
863 noit_check_update(noit_check_t *new_check,
864                   const char *target,
865                   const char *name,
866                   const char *filterset,
867                   noit_hash_table *config,
868                   noit_hash_table **mconfigs,
869                   u_int32_t period,
870                   u_int32_t timeout,
871                   const char *oncheck,
872                   int flags) {
873   int mask = NP_DISABLED | NP_UNCONFIG;
874
875   assert(name);
876   new_check->generation = __config_load_generation;
877   if(new_check->target) free(new_check->target);
878   new_check->target = strdup(target);
879
880   // apply resolution flags to check.
881   if (flags & NP_PREFER_IPV6)
882     new_check->flags |= NP_PREFER_IPV6;
883   else
884     new_check->flags &= ~NP_PREFER_IPV6;
885   if (flags & NP_SINGLE_RESOLVE)
886     new_check->flags |= NP_SINGLE_RESOLVE;
887   else
888     new_check->flags &= ~NP_SINGLE_RESOLVE;
889   if (flags & NP_RESOLVE)
890     new_check->flags |= NP_RESOLVE;
891   else
892     new_check->flags &= ~NP_RESOLVE;
893
894   /* This sets both the name and the target_addr */
895   if(noit_check_set_ip(new_check, target, name)) {
896     noit_boolean should_resolve;
897     new_check->flags |= NP_RESOLVE;
898     new_check->flags &= ~NP_RESOLVED;
899     if(noit_conf_should_resolve_targets(&should_resolve) && !should_resolve)
900       flags |= NP_DISABLED | NP_UNCONFIG;
901     noit_check_resolve(new_check);
902   }
903
904   if(new_check->filterset) free(new_check->filterset);
905   new_check->filterset = filterset ? strdup(filterset): NULL;
906
907   if(config != NULL) {
908     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
909     const char *k;
910     int klen;
911     void *data;
912     if(new_check->config) noit_hash_delete_all(new_check->config, free, free);
913     else new_check->config = calloc(1, sizeof(*new_check->config));
914     while(noit_hash_next(config, &iter, &k, &klen, &data)) {
915       noit_hash_store(new_check->config, strdup(k), klen, strdup((char *)data));
916     }
917   }
918   if(mconfigs != NULL) {
919     int i;
920     for(i=0; i<reg_module_id; i++) {
921       noit_hash_table *t;
922       if(NULL != (t = noit_check_get_module_config(new_check, i))) {
923         noit_check_set_module_config(new_check, i, NULL);
924         noit_hash_destroy(t, free, free);
925         free(t);
926       }
927       if(mconfigs[i]) {
928         noit_hash_table *t = calloc(1, sizeof(*new_check->config));
929         noit_hash_merge_as_dict(t, mconfigs[i]);
930         noit_check_set_module_config(new_check, i, t);
931       }
932     }
933   }
934   if(new_check->oncheck) free(new_check->oncheck);
935   new_check->oncheck = oncheck ? strdup(oncheck) : NULL;
936   new_check->period = period;
937   new_check->timeout = timeout;
938
939   /* Unset what could be set.. then set what should be set */
940   new_check->flags = (new_check->flags & ~mask) | flags;
941
942   noit_check_add_to_list(new_check, NULL);
943   noit_check_log_check(new_check);
944   return 0;
945 }
946 int
947 noit_poller_schedule(const char *target,
948                      const char *module,
949                      const char *name,
950                      const char *filterset,
951                      noit_hash_table *config,
952                      noit_hash_table **mconfigs,
953                      u_int32_t period,
954                      u_int32_t timeout,
955                      const char *oncheck,
956                      int flags,
957                      uuid_t in,
958                      uuid_t out) {
959   noit_check_t *new_check;
960   new_check = calloc(1, sizeof(*new_check));
961   if(!new_check) return -1;
962
963   /* The module and the UUID can never be changed */
964   new_check->module = strdup(module);
965   if(uuid_is_null(in))
966     uuid_generate(new_check->checkid);
967   else
968     uuid_copy(new_check->checkid, in);
969
970   noit_check_update(new_check, target, name, filterset, config, mconfigs,
971                     period, timeout, oncheck, flags);
972   assert(noit_hash_store(&polls,
973                          (char *)new_check->checkid, UUID_SIZE,
974                          new_check));
975   uuid_copy(out, new_check->checkid);
976
977   return 0;
978 }
979
980 /* A quick little list of recycleable checks.  This list never really
981  * grows large, so no sense in thinking too hard about the algorithmic
982  * complexity.
983  */
984 struct _checker_rcb {
985   noit_check_t *checker;
986   struct _checker_rcb *next;
987 };
988 static struct _checker_rcb *checker_rcb = NULL;
989 static void recycle_check(noit_check_t *checker) {
990   struct _checker_rcb *n = malloc(sizeof(*n));
991   n->checker = checker;
992   n->next = checker_rcb;
993   checker_rcb = n;
994 }
995 void
996 free_metric(metric_t *m) {
997   if(!m) return;
998   if(m->metric_name) free(m->metric_name);
999   if(m->metric_value.i) free(m->metric_value.i);
1000   free(m);
1001 }
1002 void
1003 noit_poller_free_check(noit_check_t *checker) {
1004   noit_module_t *mod;
1005
1006   if(checker->flags & NP_RUNNING) {
1007     recycle_check(checker);
1008     return;
1009   }
1010
1011   mod = noit_module_lookup(checker->module);
1012   if(mod && mod->cleanup) mod->cleanup(mod, checker);
1013   if(checker->fire_event) {
1014      eventer_remove(checker->fire_event);
1015      free(checker->fire_event->closure);
1016      eventer_free(checker->fire_event);
1017      checker->fire_event = NULL;
1018   }
1019   if(checker->closure) free(checker->closure);
1020   if(checker->target) free(checker->target);
1021   if(checker->module) free(checker->module);
1022   if(checker->name) free(checker->name);
1023   if(checker->config) {
1024     noit_hash_destroy(checker->config, free, free);
1025     free(checker->config);
1026     checker->config = NULL;
1027   }
1028   if(checker->module_metadata) {
1029     int i;
1030     for(i=0; i<reg_module_id; i++) {
1031       struct vp_w_free *tuple;
1032       tuple = checker->module_metadata[i];
1033       if(tuple) {
1034         if(tuple->freefunc) tuple->freefunc(tuple->ptr);
1035         free(tuple);
1036       }
1037     }
1038     free(checker->module_metadata);
1039   }
1040   if(checker->module_configs) {
1041     int i;
1042     for(i=0; i<reg_module_id; i++) {
1043       if(checker->module_configs[i]) {
1044         noit_hash_destroy(checker->module_configs[i], free, free);
1045         free(checker->module_configs[i]);
1046       }
1047     }
1048     free(checker->module_configs);
1049   }
1050   if(checker->stats.inprogress.status) free(checker->stats.inprogress.status);
1051   noit_hash_destroy(&checker->stats.inprogress.metrics, NULL,
1052                     (void (*)(void *))free_metric);
1053   if(checker->stats.current.status) free(checker->stats.current.status);
1054   noit_hash_destroy(&checker->stats.current.metrics, NULL,
1055                     (void (*)(void *))free_metric);
1056   if(checker->stats.previous.status) free(checker->stats.previous.status);
1057   noit_hash_destroy(&checker->stats.previous.metrics, NULL,
1058                     (void (*)(void *))free_metric);
1059   free(checker);
1060 }
1061 static int
1062 check_recycle_bin_processor(eventer_t e, int mask, void *closure,
1063                             struct timeval *now) {
1064   static struct timeval one_minute = { RECYCLE_INTERVAL, 0L };
1065   struct _checker_rcb *prev = NULL, *curr = checker_rcb;
1066   noitL(noit_debug, "Scanning check recycle bin\n");
1067   while(curr) {
1068     if(!(curr->checker->flags & NP_RUNNING)) {
1069       noitL(noit_debug, "Check is ready to free.\n");
1070       noit_poller_free_check(curr->checker);
1071       if(prev) prev->next = curr->next;
1072       else checker_rcb = curr->next;
1073       free(curr);
1074       curr = prev ? prev->next : checker_rcb;
1075     }
1076     else {
1077       prev = curr;
1078       curr = curr->next;
1079     }
1080   }
1081   add_timeval(*now, one_minute, &e->whence);
1082   return EVENTER_TIMER;
1083 }
1084
1085 int
1086 noit_poller_deschedule(uuid_t in) {
1087   void *vcheck;
1088   noit_check_t *checker;
1089   if(noit_hash_retrieve(&polls,
1090                         (char *)in, UUID_SIZE,
1091                         &vcheck) == 0) {
1092     return -1;
1093   }
1094   checker = (noit_check_t *)vcheck;
1095   checker->flags |= (NP_DISABLED|NP_KILLED);
1096
1097   noit_check_log_delete(checker);
1098
1099   noit_skiplist_remove(&polls_by_name, checker, NULL);
1100   noit_hash_delete(&polls, (char *)in, UUID_SIZE, NULL, NULL);
1101
1102   noit_poller_free_check(checker);
1103   return 0;
1104 }
1105
1106 noit_check_t *
1107 noit_poller_lookup(uuid_t in) {
1108   void *vcheck;
1109   if(noit_hash_retrieve(&polls, (char *)in, UUID_SIZE, &vcheck))
1110     return (noit_check_t *)vcheck;
1111   return NULL;
1112 }
1113 noit_check_t *
1114 noit_poller_lookup_by_name(char *target, char *name) {
1115   noit_check_t *check, *tmp_check;
1116   tmp_check = calloc(1, sizeof(*tmp_check));
1117   tmp_check->target = target;
1118   tmp_check->name = name;
1119   check = noit_skiplist_find(&polls_by_name, tmp_check, NULL);
1120   free(tmp_check);
1121   return check;
1122 }
1123 int
1124 noit_poller_target_ip_do(const char *target_ip,
1125                          int (*f)(noit_check_t *, void *),
1126                          void *closure) {
1127   int count = 0;
1128   noit_check_t pivot;
1129   noit_skiplist *tlist;
1130   noit_skiplist_node *next;
1131
1132   tlist = noit_skiplist_find(polls_by_name.index,
1133                              __check_target_ip_compare, NULL);
1134
1135   memset(&pivot, 0, sizeof(pivot));
1136   strlcpy(pivot.target_ip, (char*)target_ip, sizeof(pivot.target_ip));
1137   pivot.name = "";
1138   pivot.target = "";
1139   noit_skiplist_find_neighbors(tlist, &pivot, NULL, NULL, &next);
1140   while(next && next->data) {
1141     noit_check_t *check = next->data;
1142     if(strcmp(check->target_ip, target_ip)) break;
1143     count += f(check,closure);
1144     noit_skiplist_next(tlist, &next);
1145   }
1146   return count;
1147 }
1148 int
1149 noit_poller_target_do(const char *target, int (*f)(noit_check_t *, void *),
1150                       void *closure) {
1151   int count = 0;
1152   noit_check_t pivot;
1153   noit_skiplist *tlist;
1154   noit_skiplist_node *next;
1155
1156   tlist = noit_skiplist_find(polls_by_name.index,
1157                              __check_target_compare, NULL);
1158
1159   memset(&pivot, 0, sizeof(pivot));
1160   pivot.name = "";
1161   pivot.target = (char *)target;
1162   noit_skiplist_find_neighbors(tlist, &pivot, NULL, NULL, &next);
1163   while(next && next->data) {
1164     noit_check_t *check = next->data;
1165     if(strcmp(check->target, target)) break;
1166     count += f(check,closure);
1167     noit_skiplist_next(tlist, &next);
1168   }
1169   return count;
1170 }
1171
1172 int
1173 noit_poller_do(int (*f)(noit_check_t *, void *),
1174                void *closure) {
1175   noit_skiplist_node *iter;
1176   int count = 0;
1177   for(iter = noit_skiplist_getlist(&polls_by_name); iter;
1178       noit_skiplist_next(&polls_by_name, &iter)) {
1179     count += f((noit_check_t *)iter->data, closure);
1180   }
1181   return count;
1182 }
1183
1184 struct ip_module_collector_crutch {
1185   noit_check_t **array;
1186   const char *module;
1187   int idx;
1188   int allocd;
1189 };
1190 static int ip_module_collector(noit_check_t *check, void *cl) {
1191   struct ip_module_collector_crutch *c = cl;
1192   if(c->idx >= c->allocd) return 0;
1193   if(strcmp(check->module, c->module)) return 0;
1194   c->array[c->idx++] = check;
1195   return 1;
1196 }
1197 int
1198 noit_poller_lookup_by_ip_module(const char *ip, const char *mod,
1199                                 noit_check_t **checks, int nchecks) {
1200   struct ip_module_collector_crutch crutch;
1201   crutch.array = checks;
1202   crutch.allocd = nchecks;
1203   crutch.idx = 0;
1204   crutch.module = mod;
1205   return noit_poller_target_ip_do(ip, ip_module_collector, &crutch);
1206 }
1207 int
1208 noit_poller_lookup_by_module(const char *ip, const char *mod,
1209                              noit_check_t **checks, int nchecks) {
1210   struct ip_module_collector_crutch crutch;
1211   crutch.array = checks;
1212   crutch.allocd = nchecks;
1213   crutch.idx = 0;
1214   crutch.module = mod;
1215   return noit_poller_target_do(ip, ip_module_collector, &crutch);
1216 }
1217
1218
1219 int
1220 noit_check_xpath(char *xpath, int len,
1221                  const char *base, const char *arg) {
1222   uuid_t checkid;
1223   int base_trailing_slash;
1224   char argcopy[1024], *target, *module, *name;
1225
1226   base_trailing_slash = (base[strlen(base)-1] == '/');
1227   xpath[0] = '\0';
1228   argcopy[0] = '\0';
1229   if(arg) strlcpy(argcopy, arg, sizeof(argcopy));
1230
1231   if(uuid_parse(argcopy, checkid) == 0) {
1232     /* If they kill by uuid, we'll seek and destroy -- find it anywhere */
1233     snprintf(xpath, len, "/noit/checks%s%s/check[@uuid=\"%s\"]",
1234              base, base_trailing_slash ? "" : "/", argcopy);
1235   }
1236   else if((module = strchr(argcopy, '`')) != NULL) {
1237     noit_check_t *check;
1238     char uuid_str[37];
1239     target = argcopy;
1240     *module++ = '\0';
1241     if((name = strchr(module+1, '`')) == NULL)
1242       name = module;
1243     else
1244       name++;
1245     check = noit_poller_lookup_by_name(target, name);
1246     if(!check) {
1247       return -1;
1248     }
1249     uuid_unparse_lower(check->checkid, uuid_str);
1250     snprintf(xpath, len, "/noit/checks%s%s/check[@uuid=\"%s\"]",
1251              base, base_trailing_slash ? "" : "/", uuid_str);
1252   }
1253   return strlen(xpath);
1254 }
1255
1256 static int
1257 bad_check_initiate(noit_module_t *self, noit_check_t *check,
1258                    int once, noit_check_t *cause) {
1259   /* self is likely null here -- why it is bad, in fact */
1260   /* this is only suitable to call in one-offs */
1261   stats_t current;
1262   char buff[256];
1263   if(!once) return -1;
1264   if(!check) return -1;
1265   assert(!(check->flags & NP_RUNNING));
1266   check->flags |= NP_RUNNING;
1267   noit_check_stats_clear(check, &current);
1268   gettimeofday(&current.whence, NULL);
1269   current.duration = 0;
1270   current.available = NP_UNKNOWN;
1271   current.state = NP_UNKNOWN;
1272   snprintf(buff, sizeof(buff), "check[%s] implementation offline",
1273            check->module);
1274   current.status = buff;
1275   noit_check_set_stats(check, &current);
1276   check->flags &= ~NP_RUNNING;
1277   return 0;
1278 }
1279 void
1280 noit_check_stats_clear(noit_check_t *check, stats_t *s) {
1281   memset(s, 0, sizeof(*s));
1282   s->state = NP_UNKNOWN;
1283   s->available = NP_UNKNOWN;
1284 }
1285
1286 void
1287 __stats_add_metric(stats_t *newstate, metric_t *m) {
1288   noit_hash_replace(&newstate->metrics, m->metric_name, strlen(m->metric_name),
1289                     m, NULL, (void (*)(void *))free_metric);
1290 }
1291
1292 static size_t
1293 noit_metric_sizes(metric_type_t type, const void *value) {
1294   switch(type) {
1295     case METRIC_INT32:
1296     case METRIC_UINT32:
1297       return sizeof(int32_t);
1298     case METRIC_INT64:
1299     case METRIC_UINT64:
1300       return sizeof(int64_t);
1301     case METRIC_DOUBLE:
1302       return sizeof(double);
1303     case METRIC_STRING: {
1304       int len = strlen((char*)value) + 1;
1305       return ((len >= text_size_limit) ? text_size_limit+1 : len);
1306     }
1307     case METRIC_GUESS:
1308       break;
1309   }
1310   assert(type != type);
1311   return 0;
1312 }
1313 static metric_type_t
1314 noit_metric_guess_type(const char *s, void **replacement) {
1315   char *copy, *cp, *trailer, *rpl;
1316   int negative = 0;
1317   metric_type_t type = METRIC_STRING;
1318
1319   if(!s) return METRIC_GUESS;
1320   copy = cp = strdup(s);
1321
1322   /* TRIM the string */
1323   while(*cp && isspace(*cp)) cp++; /* ltrim */
1324   s = cp; /* found a good starting point */
1325   while(*cp) cp++; /* advance to \0 */
1326   cp--; /* back up one */
1327   while(cp > s && isspace(*cp)) *cp-- = '\0'; /* rtrim */
1328
1329   /* Find the first space */
1330   cp = (char *)s;
1331   while(*cp && !isspace(*cp)) cp++;
1332   trailer = cp;
1333   cp--; /* backup one */
1334   if(cp > s && *cp == '%') *cp-- = '\0'; /* chop a last % is there is one */
1335
1336   while(*trailer && isspace(*trailer)) *trailer++ = '\0'; /* rtrim */
1337
1338   /* string was       '  -1.23e-01%  inodes used  ' */
1339   /* copy is (~ = \0) '  -1.23e-01~  inodes used~~' */
1340   /*                     ^           ^              */
1341   /*                     s           trailer        */
1342
1343   /* So, the trailer must not contain numbers */
1344   while(*trailer) { if(isdigit(*trailer)) goto notanumber; trailer++; }
1345
1346   /* And the 's' must be of the form:
1347    *  0) may start with a sign [-+]?
1348    *  1) [1-9][0-9]*
1349    *  2) [0]?.[0-9]+
1350    *  3) 0
1351    *  4) [1-9][0-9]*.[0-9]+
1352    *  5) all of the above ending with e[+-][0-9]+
1353    */
1354    rpl = (char *)s;
1355    /* CASE 0 */
1356    if(s[0] == '-' || s[0] == '+') {
1357      if(s[0] == '-') negative = 1;
1358      s++;
1359    }
1360
1361    if(s[0] == '.') goto decimal; /* CASE 2 */
1362    if(s[0] == '0') { /* CASE 2 & 3 */
1363      s++;
1364      if(!s[0]) goto scanint; /* CASE 3 */
1365      if(s[0] == '.') goto decimal; /* CASE 2 */
1366      goto notanumber;
1367    }
1368    if(s[0] >= '1' && s[0] <= '9') { /* CASE 1 & 4 */
1369      s++;
1370      while(isdigit(s[0])) s++; /* CASE 1 & 4 */
1371      if(!s[0]) goto scanint; /* CASE 1 */
1372      if(s[0] == '.') goto decimal; /* CASE 4 */
1373      goto notanumber;
1374    }
1375    /* Not case 1,2,3,4 */
1376    goto notanumber;
1377
1378   decimal:
1379    s++;
1380    if(!isdigit(s[0])) goto notanumber;
1381    s++;
1382    while(isdigit(s[0])) s++;
1383    if(!s[0]) goto scandouble;
1384    if(s[0] == 'e' || s[0] == 'E') goto exponent; /* CASE 5 */
1385    goto notanumber;
1386
1387   exponent:
1388    s++;
1389    if(s[0] != '-' && s[0] != '+') goto notanumber;
1390    s++;
1391    if(!isdigit(s[0])) goto notanumber;
1392    s++;
1393    while(isdigit(s[0])) s++;
1394    if(!s[0]) goto scandouble;
1395    goto notanumber;
1396
1397  scanint:
1398    if(negative) {
1399      int64_t *v;
1400      v = calloc(1, sizeof(*v));
1401      *v = strtoll(rpl, NULL, 10);
1402      *replacement = v;
1403      type = METRIC_INT64;
1404      goto alldone;
1405    }
1406    else {
1407      u_int64_t *v;
1408      v = calloc(1, sizeof(*v));
1409      *v = strtoull(rpl, NULL, 10);
1410      *replacement = v;
1411      type = METRIC_UINT64;
1412      goto alldone;
1413    }
1414  scandouble:
1415    {
1416      double *v;
1417      v = calloc(1, sizeof(*v));
1418      *v = strtod(rpl, NULL);
1419      *replacement = v;
1420      type = METRIC_DOUBLE;
1421      goto alldone;
1422    }
1423
1424  alldone:
1425  notanumber:
1426   free(copy);
1427   return type;
1428 }
1429 int
1430 noit_stats_populate_metric(metric_t *m, const char *name, metric_type_t type,
1431                            const void *value) {
1432   void *replacement = NULL;
1433   if(type == METRIC_GUESS)
1434     type = noit_metric_guess_type((char *)value, &replacement);
1435   if(type == METRIC_GUESS) return -1;
1436
1437   m->metric_name = strdup(name);
1438   m->metric_type = type;
1439   if(replacement)
1440     m->metric_value.vp = replacement;
1441   else if(value) {
1442     size_t len;
1443     len = noit_metric_sizes(type, value);
1444     m->metric_value.vp = calloc(1, len);
1445     memcpy(m->metric_value.vp, value, len);
1446     if (type == METRIC_STRING) {
1447       m->metric_value.s[len-1] = 0;
1448     }
1449   }
1450   return 0;
1451 }
1452
1453 metric_t *
1454 noit_stats_get_metric(noit_check_t *check,
1455                       stats_t *newstate, const char *name) {
1456   void *v;
1457   if(noit_hash_retrieve(&newstate->metrics, name, strlen(name), &v))
1458     return (metric_t *)v;
1459   return NULL;
1460 }
1461
1462 void
1463 noit_stats_set_metric(noit_check_t *check,
1464                       stats_t *newstate, const char *name, metric_type_t type,
1465                       const void *value) {
1466   metric_t *m = calloc(1, sizeof(*m));
1467   if(noit_stats_populate_metric(m, name, type, value)) {
1468     free_metric(m);
1469     return;
1470   }
1471   noit_check_metric_count_add(1);
1472   check_stats_set_metric_hook_invoke(check, newstate, m);
1473   __stats_add_metric(newstate, m);
1474 }
1475 void
1476 noit_stats_set_metric_coerce(noit_check_t *check,
1477                              stats_t *stat, const char *name, metric_type_t t,
1478                              const char *v) {
1479   char *endptr;
1480   if(v == NULL) {
1481    bogus:
1482     check_stats_set_metric_coerce_hook_invoke(check, stat, name, t, v, noit_false);
1483     noit_stats_set_metric(check, stat, name, t, NULL);
1484     return;
1485   }
1486   switch(t) {
1487     case METRIC_STRING:
1488       noit_stats_set_metric(check, stat, name, t, v);
1489       break;
1490     case METRIC_INT32:
1491     {
1492       int32_t val;
1493       val = strtol(v, &endptr, 10);
1494       if(endptr == v) goto bogus;
1495       noit_stats_set_metric(check, stat, name, t, &val);
1496       break;
1497     }
1498     case METRIC_UINT32:
1499     {
1500       u_int32_t val;
1501       val = strtoul(v, &endptr, 10);
1502       if(endptr == v) goto bogus;
1503       noit_stats_set_metric(check, stat, name, t, &val);
1504       break;
1505     }
1506     case METRIC_INT64:
1507     {
1508       int64_t val;
1509       val = strtoll(v, &endptr, 10);
1510       if(endptr == v) goto bogus;
1511       noit_stats_set_metric(check, stat, name, t, &val);
1512       break;
1513     }
1514     case METRIC_UINT64:
1515     {
1516       u_int64_t val;
1517       val = strtoull(v, &endptr, 10);
1518       if(endptr == v) goto bogus;
1519       noit_stats_set_metric(check, stat, name, t, &val);
1520       break;
1521     }
1522     case METRIC_DOUBLE:
1523     {
1524       double val;
1525       val = strtod(v, &endptr);
1526       if(endptr == v) goto bogus;
1527       noit_stats_set_metric(check, stat, name, t, &val);
1528       break;
1529     }
1530     case METRIC_GUESS:
1531       noit_stats_set_metric(check, stat, name, t, v);
1532       break;
1533   }
1534   check_stats_set_metric_coerce_hook_invoke(check, stat, name, t, v, noit_true);
1535 }
1536 void
1537 noit_stats_log_immediate_metric(noit_check_t *check,
1538                                 const char *name, metric_type_t type,
1539                                 void *value) {
1540   struct timeval now;
1541   metric_t *m = calloc(1, sizeof(*m));
1542   if(noit_stats_populate_metric(m, name, type, value)) {
1543     free_metric(m);
1544     return;
1545   }
1546   gettimeofday(&now, NULL);
1547   noit_check_log_metric(check, &now, m);
1548   free_metric(m);
1549 }
1550
1551 void
1552 noit_check_passive_set_stats(noit_check_t *check, stats_t *newstate) {
1553   noit_skiplist_node *next;
1554   noit_check_t n;
1555
1556   uuid_copy(n.checkid, check->checkid);
1557   n.period = 0;
1558
1559   noit_check_set_stats(check,newstate);
1560   noit_skiplist_find_neighbors(&watchlist, &n, NULL, NULL, &next);
1561   while(next && next->data) {
1562     stats_t backup;
1563     noit_check_t *wcheck = next->data;
1564     if(uuid_compare(n.checkid, wcheck->checkid)) break;
1565
1566     /* We advance before we use because the functions below could
1567      * destroy the watchlist item we're looking at right now
1568      */
1569     noit_skiplist_next(&watchlist, &next);
1570
1571     /* Swap the real check's stats into place */
1572     memcpy(&backup, &wcheck->stats.current, sizeof(stats_t));
1573     memcpy(&wcheck->stats.current, &check->stats.current, sizeof(stats_t));
1574
1575     if(check_passive_log_stats_hook_invoke(check) == NOIT_HOOK_CONTINUE) {
1576       /* Write out our status */
1577       noit_check_log_status(wcheck);
1578       /* Write out all metrics */
1579       noit_check_log_metrics(wcheck);
1580     }
1581     /* Swap them back out */
1582     memcpy(&wcheck->stats.current, &backup, sizeof(stats_t));
1583   }
1584 }
1585 void
1586 noit_check_set_stats(noit_check_t *check, stats_t *newstate) {
1587   int report_change = 0;
1588   char *cp;
1589   dep_list_t *dep;
1590   if(check->stats.previous.status)
1591     free(check->stats.previous.status);
1592   noit_hash_destroy(&check->stats.previous.metrics, NULL,
1593                     (void (*)(void *))free_metric);
1594   memcpy(&check->stats.previous, &check->stats.current, sizeof(stats_t));
1595   if(newstate)
1596     memcpy(&check->stats.current, newstate, sizeof(stats_t));
1597   if(check->stats.current.status)
1598     check->stats.current.status = strdup(check->stats.current.status);
1599   for(cp = check->stats.current.status; cp && *cp; cp++)
1600     if(*cp == '\r' || *cp == '\n') *cp = ' ';
1601
1602   /* check for state changes */
1603   if(check->stats.current.available != NP_UNKNOWN &&
1604      check->stats.previous.available != NP_UNKNOWN &&
1605      check->stats.current.available != check->stats.previous.available)
1606     report_change = 1;
1607   if(check->stats.current.state != NP_UNKNOWN &&
1608      check->stats.previous.state != NP_UNKNOWN &&
1609      check->stats.current.state != check->stats.previous.state)
1610     report_change = 1;
1611
1612   noitL(noit_debug, "%s`%s <- [%s]\n", check->target, check->name,
1613         check->stats.current.status);
1614   if(report_change) {
1615     noitL(noit_debug, "%s`%s -> [%s:%s]\n",
1616           check->target, check->name,
1617           noit_check_available_string(check->stats.current.available),
1618           noit_check_state_string(check->stats.current.state));
1619   }
1620
1621   if(NOIT_CHECK_STATUS_ENABLED()) {
1622     char id[UUID_STR_LEN+1];
1623     uuid_unparse_lower(check->checkid, id);
1624     NOIT_CHECK_STATUS(id, check->module, check->name, check->target,
1625                       check->stats.current.available,
1626                       check->stats.current.state,
1627                       check->stats.current.status);
1628   }
1629
1630   if(check_log_stats_hook_invoke(check) == NOIT_HOOK_CONTINUE) {
1631     /* Write out the bundled information */
1632     noit_check_log_bundle(check);
1633   }
1634   /* count the check as complete */
1635   check_completion_count++;
1636
1637   for(dep = check->causal_checks; dep; dep = dep->next) {
1638     noit_module_t *mod;
1639     mod = noit_module_lookup(dep->check->module);
1640     if(!mod) {
1641       bad_check_initiate(mod, dep->check, 1, check);
1642     }
1643     else {
1644       noitL(noit_debug, "Firing %s`%s in response to %s`%s\n",
1645             dep->check->target, dep->check->name,
1646             check->target, check->name);
1647       if((dep->check->flags & NP_DISABLED) == 0)
1648         if(mod->initiate_check)
1649           mod->initiate_check(mod, dep->check, 1, check);
1650     }
1651   }
1652 }
1653
1654 static int
1655 noit_console_show_watchlist(noit_console_closure_t ncct,
1656                             int argc, char **argv,
1657                             noit_console_state_t *dstate,
1658                             void *closure) {
1659   noit_skiplist_node *iter, *fiter;
1660   nc_printf(ncct, "%d active watches.\n", watchlist.size);
1661   for(iter = noit_skiplist_getlist(&watchlist); iter;
1662       noit_skiplist_next(&watchlist, &iter)) {
1663     char uuid_str[UUID_STR_LEN + 1];
1664     noit_check_t *check = iter->data;
1665
1666     uuid_unparse_lower(check->checkid, uuid_str);
1667     nc_printf(ncct, "%s:\n\t[%s`%s`%s]\n\tPeriod: %dms\n\tFeeds[%d]:\n",
1668               uuid_str, check->target, check->module, check->name,
1669               check->period, check->feeds ? check->feeds->size : 0);
1670     if(check->feeds && check->feeds->size) {
1671       for(fiter = noit_skiplist_getlist(check->feeds); fiter;
1672           noit_skiplist_next(check->feeds, &fiter)) {
1673         nc_printf(ncct, "\t\t%s\n", (const char *)fiter->data);
1674       }
1675     }
1676   }
1677   return 0;
1678 }
1679
1680 static void
1681 nc_printf_check_brief(noit_console_closure_t ncct,
1682                       noit_check_t *check) {
1683   char out[512];
1684   char uuid_str[37];
1685   snprintf(out, sizeof(out), "%s`%s (%s [%x])", check->target, check->name,
1686            check->target_ip, check->flags);
1687   uuid_unparse_lower(check->checkid, uuid_str);
1688   nc_printf(ncct, "%s %s\n", uuid_str, out);
1689   if(check->stats.current.status)
1690     nc_printf(ncct, "\t%s\n", check->stats.current.status);
1691 }
1692
1693 char *
1694 noit_console_conf_check_opts(noit_console_closure_t ncct,
1695                              noit_console_state_stack_t *stack,
1696                              noit_console_state_t *dstate,
1697                              int argc, char **argv, int idx) {
1698   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1699   uuid_t key_id;
1700   int klen, i = 0;
1701   void *vcheck;
1702
1703   if(argc == 1) {
1704     if(!strncmp("new", argv[0], strlen(argv[0]))) {
1705       if(idx == i) return strdup("new");
1706       i++;
1707     }
1708     while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1709                          &vcheck)) {
1710       noit_check_t *check = (noit_check_t *)vcheck;
1711       char out[512];
1712       char uuid_str[37];
1713       snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
1714       uuid_unparse_lower(check->checkid, uuid_str);
1715       if(!strncmp(out, argv[0], strlen(argv[0]))) {
1716         if(idx == i) return strdup(out);
1717         i++;
1718       }
1719       if(!strncmp(uuid_str, argv[0], strlen(argv[0]))) {
1720         if(idx == i) return strdup(uuid_str);
1721         i++;
1722       }
1723     }
1724   }
1725   if(argc == 2) {
1726     cmd_info_t *cmd;
1727     if(!strcmp("new", argv[0])) return NULL;
1728     cmd = noit_skiplist_find(&dstate->cmds, "attribute", NULL);
1729     if(!cmd) return NULL;
1730     return noit_console_opt_delegate(ncct, stack, cmd->dstate, argc-1, argv+1, idx);
1731   }
1732   return NULL;
1733 }
1734
1735 char *
1736 noit_console_check_opts(noit_console_closure_t ncct,
1737                         noit_console_state_stack_t *stack,
1738                         noit_console_state_t *dstate,
1739                         int argc, char **argv, int idx) {
1740   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1741   uuid_t key_id;
1742   int klen, i = 0;
1743
1744   if(argc == 1) {
1745     void *vcheck;
1746     while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1747                          &vcheck)) {
1748       char out[512];
1749       char uuid_str[37];
1750       noit_check_t *check = (noit_check_t *)vcheck;
1751       snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
1752       uuid_unparse_lower(check->checkid, uuid_str);
1753       if(!strncmp(out, argv[0], strlen(argv[0]))) {
1754         if(idx == i) return strdup(out);
1755         i++;
1756       }
1757       if(!strncmp(uuid_str, argv[0], strlen(argv[0]))) {
1758         if(idx == i) return strdup(uuid_str);
1759         i++;
1760       }
1761     }
1762   }
1763   if(argc == 2) {
1764     return noit_console_opt_delegate(ncct, stack, dstate, argc-1, argv+1, idx);
1765   }
1766   return NULL;
1767 }
1768
1769 static int
1770 noit_console_show_checks(noit_console_closure_t ncct,
1771                          int argc, char **argv,
1772                          noit_console_state_t *dstate,
1773                          void *closure) {
1774   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1775   uuid_t key_id;
1776   int klen;
1777   void *vcheck;
1778
1779   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1780                        &vcheck)) {
1781     nc_printf_check_brief(ncct, (noit_check_t *)vcheck);
1782   }
1783   return 0;
1784 }
1785
1786 static int
1787 noit_console_short_checks_sl(noit_console_closure_t ncct,
1788                              noit_skiplist *tlist) {
1789   noit_skiplist_node *iter;
1790   for(iter = noit_skiplist_getlist(tlist); iter;
1791       noit_skiplist_next(tlist, &iter)) {
1792     nc_printf_check_brief(ncct, (noit_check_t *)iter->data);
1793   }
1794   return 0;
1795 }
1796 static int
1797 noit_console_show_checks_name(noit_console_closure_t ncct,
1798                               int argc, char **argv,
1799                               noit_console_state_t *dstate,
1800                               void *closure) {
1801   return noit_console_short_checks_sl(ncct, &polls_by_name);
1802 }
1803
1804 static int
1805 noit_console_show_checks_target(noit_console_closure_t ncct,
1806                                    int argc, char **argv,
1807                                    noit_console_state_t *dstate,
1808                                    void *closure) {
1809   return noit_console_short_checks_sl(ncct,
1810            noit_skiplist_find(polls_by_name.index,
1811            __check_target_compare, NULL));
1812 }
1813
1814 static int
1815 noit_console_show_checks_target_ip(noit_console_closure_t ncct,
1816                                    int argc, char **argv,
1817                                    noit_console_state_t *dstate,
1818                                    void *closure) {
1819   return noit_console_short_checks_sl(ncct,
1820            noit_skiplist_find(polls_by_name.index,
1821            __check_target_ip_compare, NULL));
1822 }
1823
1824 static void
1825 register_console_check_commands() {
1826   noit_console_state_t *tl;
1827   cmd_info_t *showcmd;
1828
1829   tl = noit_console_state_initial();
1830   showcmd = noit_console_state_get_cmd(tl, "show");
1831   assert(showcmd && showcmd->dstate);
1832
1833   noit_console_state_add_cmd(showcmd->dstate,
1834     NCSCMD("timing_slots", noit_console_show_timing_slots, NULL, NULL, NULL));
1835
1836   noit_console_state_add_cmd(showcmd->dstate,
1837     NCSCMD("checks", noit_console_show_checks, NULL, NULL, NULL));
1838
1839   noit_console_state_add_cmd(showcmd->dstate,
1840     NCSCMD("checks:name", noit_console_show_checks_name, NULL,
1841            NULL, NULL));
1842
1843   noit_console_state_add_cmd(showcmd->dstate,
1844     NCSCMD("checks:target", noit_console_show_checks_target, NULL,
1845            NULL, NULL));
1846
1847   noit_console_state_add_cmd(showcmd->dstate,
1848     NCSCMD("checks:target_ip", noit_console_show_checks_target_ip, NULL,
1849            NULL, NULL));
1850
1851   noit_console_state_add_cmd(showcmd->dstate,
1852     NCSCMD("watches", noit_console_show_watchlist, NULL, NULL, NULL));
1853 }
1854
1855 int
1856 noit_check_register_module(const char *name) {
1857   int i;
1858   for(i=0; i<reg_module_id; i++)
1859     if(!strcmp(reg_module_names[i], name)) return i;
1860   if(reg_module_id >= MAX_MODULE_REGISTRATIONS) return -1;
1861   noitL(noit_debug, "Registered module %s as %d\n", name, i);
1862   i = reg_module_id++;
1863   reg_module_names[i] = strdup(name);
1864   noit_conf_set_namespace(reg_module_names[i]);
1865   return i;
1866 }
1867 int
1868 noit_check_registered_module_cnt() {
1869   return reg_module_id;
1870 }
1871 const char *
1872 noit_check_registered_module(int idx) {
1873   if(reg_module_used < 0) reg_module_used = reg_module_id;
1874   assert(reg_module_used == reg_module_id);
1875   if(idx >= reg_module_id || idx < 0) return NULL;
1876   return reg_module_names[idx];
1877 }
1878
1879 void
1880 noit_check_set_module_metadata(noit_check_t *c, int idx, void *md, void (*freefunc)(void *)) {
1881   struct vp_w_free *tuple;
1882   if(reg_module_used < 0) reg_module_used = reg_module_id;
1883   assert(reg_module_used == reg_module_id);
1884   if(idx >= reg_module_id || idx < 0) return;
1885   if(!c->module_metadata) c->module_metadata = calloc(reg_module_id, sizeof(void *));
1886   c->module_metadata[idx] = calloc(1, sizeof(struct vp_w_free));
1887   tuple = c->module_metadata[idx];
1888   tuple->ptr = md;
1889   tuple->freefunc = freefunc;
1890 }
1891 void
1892 noit_check_set_module_config(noit_check_t *c, int idx, noit_hash_table *config) {
1893   if(reg_module_used < 0) reg_module_used = reg_module_id;
1894   assert(reg_module_used == reg_module_id);
1895   if(idx >= reg_module_id || idx < 0) return;
1896   if(!c->module_configs) c->module_configs = calloc(reg_module_id, sizeof(noit_hash_table *));
1897   c->module_configs[idx] = config;
1898 }
1899 void *
1900 noit_check_get_module_metadata(noit_check_t *c, int idx) {
1901   struct vp_w_free *tuple;
1902   if(reg_module_used < 0) reg_module_used = reg_module_id;
1903   assert(reg_module_used == reg_module_id);
1904   if(idx >= reg_module_id || idx < 0 || !c->module_metadata) return NULL;
1905   tuple = c->module_metadata[idx];
1906   return tuple ? tuple->ptr : NULL;
1907 }
1908 noit_hash_table *
1909 noit_check_get_module_config(noit_check_t *c, int idx) {
1910   if(reg_module_used < 0) reg_module_used = reg_module_id;
1911   assert(reg_module_used == reg_module_id);
1912   if(idx >= reg_module_id || idx < 0 || !c->module_configs) return NULL;
1913   return c->module_configs[idx];
1914 }
Note: See TracBrowser for help on using the browser.